До сих пор ответы от SO были совершенно удовлетворительными для моих проблем. Я изучаю модульное тестирование с помощью Junit и Mockito, и я хочу проверить класс сервиса, который является частью моего веб-приложения Spring. Я прочитал много учебников и статей, и у меня все еще есть проблемы с написанием правильных модульных тестов для моего уровня обслуживания. Я хотел бы знать ответы на мои вопросы, но сначала я вставляю код:Испытание весеннего сервисного модуля с использованием mockito
Класс обслуживания
public class AccountServiceImpl implements AccountService {
@Autowired
AccountDao accountDao, RoleDao roleDao, PasswordEncoder passwordEncoder, SaltSource saltSource;
@PersistenceContext
EntityManager entityManager;
public Boolean registerNewAccount(Account newAccount) {
entityManager.persist(newAccount);
newAccount.setPassword(passwordEncoder.encodePassword(newAccount.getPassword(), saltSource.getSalt(newAccount)));
setRoleToAccount("ROLE_REGISTERED", newAccount);
return checkIfUsernameExists(newAccount.getUsername());
}
public void setRoleToAccount(String roleName, Account account) {
List<Role> roles = new ArrayList<Role>();
try {
roles.add(roleDao.findRole(roleName));
} catch(RoleNotFoundException rnf) {
logger.error(rnf.getMessage());
}
account.setRoles(roles);
}
public Boolean checkIfUsernameExists(String username) {
try {
loadUserByUsername(username);
} catch(UsernameNotFoundException unf) {
return false;
}
return true;
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
Account loadedAccount = accountDao.findUsername(username);
return loadedAccount;
} catch (UserNotFoundException e) {
throw new UsernameNotFoundException("User: " + username + "not found!");
}
}
}
Мой незаконченный тест класса
@RunWith(MockitoJUnitRunner.class)
public class AccountServiceImplTest {
private AccountServiceImpl accountServiceImpl;
@Mock private Account newAccount;
@Mock private PasswordEncoder passwordEncoder;
@Mock private SaltSource saltSource;
@Mock private EntityManager entityManager;
@Mock private AccountDao accountDao;
@Mock private RoleDao roleDao;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
accountServiceImpl = new AccountServiceImpl();
ReflectionTestUtils.setField(accountServiceImpl, "entityManager", entityManager);
ReflectionTestUtils.setField(accountServiceImpl, "passwordEncoder", passwordEncoder);
ReflectionTestUtils.setField(accountServiceImpl, "saltSource", saltSource);
ReflectionTestUtils.setField(accountServiceImpl, "accountDao", accountDao);
ReflectionTestUtils.setField(accountServiceImpl, "roleDao", roleDao);
}
@Test
public void testRegisterNewAccount() {
Boolean isAccountCreatedSuccessfully = accountServiceImpl.registerNewAccount(newAccount);
verify(entityManager).persist(newAccount);
verify(newAccount).setPassword(passwordEncoder.encodePassword(newAccount.getPassword(), saltSource.getSalt(newAccount)));
assertTrue(isAccountCreatedSuccessfully);
}
@Test
public void testShouldSetRoleToAccount() throws RoleNotFoundException{
Role role = new Role(); //Maybe I can use mock here?
role.setName("ROLE_REGISTERED");
when(roleDao.findRole("ROLE_REGISTERED")).thenReturn(role);
accountServiceImpl.setRoleToAccount("ROLE_REGISTERED", newAccount);
assertTrue(newAccount.getRoles().contains(role));
}
}
Вопросы :
- Каков наилучший способ сделать модульные тесты, когда у меня есть методы в методах, подобных моему классу обслуживания? Могу ли я тестировать их отдельно, как указано выше? [Я разделил свой код на несколько методов, чтобы иметь более чистый код]
- Является ли testRegisterNewAccount() хорошим модульным тестом для моего метода обслуживания? Тест зеленый, но я не уверен в этом.
- Я получаю сбой в своем testShouldSetRoleToAccount. Что я делаю не так?
- Как проверить checkIfUsernameExists?
Может кто-то поможет мне с этим, потому что я провел пару дней, и я не сделал прогресс :(
UPDATE
Закончено тестовый класс
@RunWith(MockitoJUnitRunner.class)
public class AccountServiceImplTest extends BaseTest {
private AccountServiceImpl accountServiceImpl;
private Role role;
private Account account;
@Mock private Account newAccount;
@Mock private PasswordEncoder passwordEncoder;
@Mock private SaltSource saltSource;
@Mock private EntityManager entityManager;
@Mock private AccountDao accountDao;
@Mock private RoleDao roleDao;
@Before
public void init() {
accountServiceImpl = new AccountServiceImpl();
role = new Role();
account = new Account();
ReflectionTestUtils.setField(accountServiceImpl, "entityManager", entityManager);
ReflectionTestUtils.setField(accountServiceImpl, "passwordEncoder", passwordEncoder);
ReflectionTestUtils.setField(accountServiceImpl, "saltSource", saltSource);
ReflectionTestUtils.setField(accountServiceImpl, "accountDao", accountDao);
ReflectionTestUtils.setField(accountServiceImpl, "roleDao", roleDao);
}
@Test
public void testShouldRegisterNewAccount() {
Boolean isAccountCreatedSuccessfully = accountServiceImpl.registerNewAccount(newAccount);
verify(entityManager).persist(newAccount);
verify(newAccount).setPassword(passwordEncoder.encodePassword(newAccount.getPassword(), saltSource.getSalt(newAccount)));
assertTrue(isAccountCreatedSuccessfully);
}
@Test(expected = IllegalArgumentException.class)
public void testShouldNotRegisterNewAccount() {
doThrow(new IllegalArgumentException()).when(entityManager).persist(account);
accountServiceImpl.registerNewAccount(account);
}
@Test
public void testShouldSetRoleToAccount() throws RoleNotFoundException {
when(roleDao.findRole(anyString())).thenReturn(role);
accountServiceImpl.setRoleToAccount("ROLE_REGISTERED", account);
assertTrue(account.getRoles().contains(role));
}
@Test
public void testShouldNotSetRoleToAccount() throws RoleNotFoundException {
when(roleDao.findRole(anyString())).thenThrow(new RoleNotFoundException());
accountServiceImpl.setRoleToAccount("ROLE_RANDOM", account);
assertFalse(account.getRoles().contains(role));
}
@Test
public void testCheckIfUsernameExistsIsTrue() throws UserNotFoundException {
when(accountDao.findUsername(anyString())).thenReturn(account);
Boolean userExists = accountServiceImpl.checkIfUsernameExists(anyString());
assertTrue(userExists);
}
@Test
public void testCheckIfUsernameExistsIsFalse() throws UserNotFoundException {
when(accountDao.findUsername(anyString())).thenThrow(new UserNotFoundException());
Boolean userExists = accountServiceImpl.checkIfUsernameExists(anyString());
assertFalse(userExists);
}
@Test
public void testShouldLoadUserByUsername() throws UserNotFoundException {
when(accountDao.findUsername(anyString())).thenReturn(account);
Account foundAccount = (Account) accountServiceImpl.loadUserByUsername(anyString());
assertEquals(account, foundAccount);
}
@Test(expected = UsernameNotFoundException.class)
public void testShouldNotLoadUserByUsername() throws UserNotFoundException {
when(accountDao.findUsername(anyString())).thenThrow(new UsernameNotFoundException(null));
accountServiceImpl.loadUserByUsername(anyString());
}
}
Спасибо, Дэвид. Ответ на вопрос 1 Я полностью понимаю, и я выбрал вариант 1. О вопросе 3 Мне удалось решить проблему. Мне пришлось заменить mock Account на Account, созданный новым оператором, и это было [sic!] :). Вопрос 4 также помог мне, но у меня другие проблемы. Как вы можете видеть благодаря вашим советам, мне удалось написать тесты для всех моих методов. Они работают нормально, за исключением testShouldNotSetRoleToAccount и testShouldNotLoadUserByUsername. Оба не работают, когда «ожидается = ...». Без него все в порядке. Более того, сначала также производится ошибка en и ошибка Ошибка. Не могли бы вы помочь мне? –
Извините, мне потребовалось некоторое время, чтобы вернуться к вам. Если эти тесты терпят неудачу с ожидаемым набором исключений, это означает, что исключение фактически не выбрасывается. Вы уверены, что 'roleDao' и' accountDao' действительно были настроены на mocks? Вы можете проверить это с помощью отладчика. Кроме того, вы не используете 'anyString()' правильно - это для stubbing и проверки, а не для запуска вашего метода; Я не знаю, является ли это причиной вашей проблемы. В строках, где вы фактически запускаете свои методы, поместите фактическое значение, которое вы хотите передать, вместо 'anyString()'. –
Эх лол, я глуп. Эта ошибка, о которой я говорил, это просто информация о консоли от регистратора. В roleDao, когда исключение поймано, есть logger.error (..): P. Я внимательно изучил свой тестовый код, и теперь все в порядке. «ожидается» в testShouldNotSetRoleToAccount и testCheckIfUsernameExistsIsFalse не требуется. Позже я обновлю свой тестовый класс, и мы можем закрыть это обсуждение. Еще раз спасибо Дэвиду, ваши подсказки были очень полезны :) –