2012-01-23 4 views
11

До сих пор ответы от 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)); 
} 

} 

Вопросы :

  1. Каков наилучший способ сделать модульные тесты, когда у меня есть методы в методах, подобных моему классу обслуживания? Могу ли я тестировать их отдельно, как указано выше? [Я разделил свой код на несколько методов, чтобы иметь более чистый код]
  2. Является ли testRegisterNewAccount() хорошим модульным тестом для моего метода обслуживания? Тест зеленый, но я не уверен в этом.
  3. Я получаю сбой в своем testShouldSetRoleToAccount. Что я делаю не так?
  4. Как проверить 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()); 
} 

} 

ответ

5

Вопрос 1 - У вас есть пара вариантов здесь.

Вариант 1 - написать отдельные тесты для каждого поведения каждого публичного метода на основе того, что требуется для этого поведения. Это гарантирует, что каждый тест будет чистым и отдельным, но это означает, что логика вторичных методов (например, checkIfUsernameExists) будет выполняться дважды. В некотором смысле, это ненужное дублирование, но одним из преимуществ этого варианта является то, что если вы измените реализацию, но не требуемое поведение, у вас все еще будут хорошие тесты, основанные на поведении.

Вариант 2 - использовать шок Mockito. Это немного похоже на макет, за исключением того, что вы создаете его из реального объекта, и по умолчанию его поведение заключается в том, что все методы выполняются как обычно. Затем вы можете вырезать и проверить вторичные методы, чтобы проверить методы, которые их называют.

Вопрос 2 - Это похоже на хороший тест для случая с успехом registerNewAccount. Пожалуйста, подумайте о том, какие обстоятельства приведут к ошибке registerNewAccount и возврату false; и проверить этот случай.

Вопрос 3 - У меня не было хорошего взгляда на это; но попробуйте запустить с помощью отладчика и узнайте, в какой момент ваши объекты отличаются от ожидаемых. Если вы не можете это обработать, отправьте сообщение, и я снова посмотрю.

Вопрос 4 - Чтобы проверить отрицательный случай, заглушите свой макет AccountDao, чтобы выбросить необходимое исключение. В противном случае см. Ответы на вопрос 1.

+0

Спасибо, Дэвид. Ответ на вопрос 1 Я полностью понимаю, и я выбрал вариант 1. О вопросе 3 Мне удалось решить проблему. Мне пришлось заменить mock Account на Account, созданный новым оператором, и это было [sic!] :). Вопрос 4 также помог мне, но у меня другие проблемы. Как вы можете видеть благодаря вашим советам, мне удалось написать тесты для всех моих методов. Они работают нормально, за исключением testShouldNotSetRoleToAccount и testShouldNotLoadUserByUsername. Оба не работают, когда «ожидается = ...». Без него все в порядке. Более того, сначала также производится ошибка en и ошибка Ошибка. Не могли бы вы помочь мне? –

+0

Извините, мне потребовалось некоторое время, чтобы вернуться к вам. Если эти тесты терпят неудачу с ожидаемым набором исключений, это означает, что исключение фактически не выбрасывается. Вы уверены, что 'roleDao' и' accountDao' действительно были настроены на mocks? Вы можете проверить это с помощью отладчика. Кроме того, вы не используете 'anyString()' правильно - это для stubbing и проверки, а не для запуска вашего метода; Я не знаю, является ли это причиной вашей проблемы. В строках, где вы фактически запускаете свои методы, поместите фактическое значение, которое вы хотите передать, вместо 'anyString()'. –

+0

Эх лол, я глуп. Эта ошибка, о которой я говорил, это просто информация о консоли от регистратора. В roleDao, когда исключение поймано, есть logger.error (..): P. Я внимательно изучил свой тестовый код, и теперь все в порядке. «ожидается» в testShouldNotSetRoleToAccount и testCheckIfUsernameExistsIsFalse не требуется. Позже я обновлю свой тестовый класс, и мы можем закрыть это обсуждение. Еще раз спасибо Дэвиду, ваши подсказки были очень полезны :) –

Смежные вопросы