【问题标题】:Spring service unit testing using mockito使用 mockito 进行 Spring 服务单元测试
【发布时间】:2012-01-23 22:55:16
【问题描述】:

到目前为止,SO 的回答对我的问题完全满意。我正在学习使用 Junit 和 Mockito 进行单元测试,我想测试我的服务类,它是我的 Spring Web 应用程序的一部分。我阅读了许多教程和文章,但我仍然无法为我的服务层编写适当的单元测试。我想知道我的问题的答案,但首先我粘贴一些代码:

服务类

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());
}

}

【问题讨论】:

    标签: java spring junit mockito


    【解决方案1】:

    问题 1 - 这里有几个选项。

    选项 1 - 根据该行为所需的内容,为每个公共方法的每个行为编写单独的测试。这使每个测试保持干净和独立,但这确实意味着辅助方法(例如checkIfUsernameExists)中的逻辑将被执行两次。从某种意义上说,这是不必要的重复,但此选项的一个优点是,如果您更改了实现,但没有更改所需的行为,您仍然可以根据行为进行良好的测试。

    选项 2 - 使用 Mockito Spy。这有点像一个模拟,除了你从一个真实的对象创建它,它的默认行为是所有方法都照常运行。然后,您可以存根并验证辅助方法,以测试调用它们的方法。

    问题 2 - 这看起来像是对 registerNewAccount 的“成功”案例的一个很好的测试。请想一想什么情况会导致registerNewAccount失败并返回false;并测试这个案例。

    问题 3 - 我没仔细看这个;但是尝试使用调试器运行,并找出您的对象与您期望的不同之处。如果你不能解决,再发一次,我再看看。

    问题 4 - 要测试否定情况,请将您的 AccountDao 模拟存根以抛出所需的异常。否则,请参阅我对问题 1 的回答。

    【讨论】:

    • 谢谢大卫。回答问题 1 我完全理解,我选择了选项 1。关于问题 3,我设法解决了一个问题。我不得不用新操作员创建的帐户替换模拟帐户,就是它[原文如此!] :)。问题 4 也帮助了我,但我还有其他问题。正如您所看到的,感谢您的提示,我设法为我的所有方法编写了测试。除了 testShouldNotSetRoleToAccount 和 testShouldNotLoadUserByUsername 之外,它们工作正常。当有“expected = ...”时,两者都失败了。没有它就可以了。而且先也犯了 en 错误和 test 是 错误。你能帮帮我吗?
    • 抱歉,我花了一段时间才回复您。如果这些测试因预期的异常集而失败,则意味着该异常实际上并未被抛出。您确定 roleDaoaccountDao 实际上已设置为模拟吗?您可以使用调试器进行检查。此外,您没有正确使用anyString() - 这是用于存根和验证,而不是实际运行您的方法;我不知道这是否是你的问题的原因。在实际运行方法的行中,输入要传递的实际值,而不是 anyString()
    • 呵呵,我很傻。我所说的这个错误只是来自记录器的控制台信息。在捕获异常时的 roleDao 中有 logger.error(..) :P。我彻底查看了我的测试代码,现在一切正常。不需要 testShouldNotSetRoleToAccount 和 testCheckIfUsernameExistsIsFalse 中的“预期”。稍后我将更新我的测试课程,我们可以结束这个讨论。再次感谢大卫,你的提示很有帮助:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    • 1970-01-01
    • 2019-07-07
    相关资源
    最近更新 更多