Тестирование сервисной единицы Spring с использованием mockito - PullRequest
12 голосов
/ 24 января 2012

До сих пор ответы от SO полностью удовлетворяли мои проблемы.Я изучаю юнит-тестирование с Junit и Mockito и хочу протестировать свой класс обслуживания, который является частью моего веб-приложения Spring.Я прочитал много учебников и статей, и у меня все еще есть проблемы с написанием надлежащих модульных тестов для моего сервисного уровня.Я хотел бы знать ответы на свои вопросы, но сначала я вставляю некоторый код:

Класс обслуживания

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?

Может быть, кто-то мне поможет с этим, потому что я провел пару дней, и у меня не было прогресса :(


ОБНОВЛЕНИЕ

Законченный тестовый класс

@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 Ответ

5 голосов
/ 24 января 2012

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...