《Java单元测试实战》——编写方法:Java编程技巧之单元测试用例编写流程(1) https://developer.aliyun.com/article/1232420?groupCode=java
3) 一个有依赖的单元测试
一个有依赖的单元测试,需要四大步骤:
• 定义对象:定义测试对象、模拟依赖对象、注入依赖对象;
• 模拟方法:模拟参数或返回值、模拟依赖方法;
• 调用方法:传入参数对象、调用测试方法、验证返回值或异常;
• 验证方法:验证依赖方法、验证方法参数、验证依赖对象。
案例代码:
/** * 用户服务类 */ @Service public class UserService { /** 定义依赖对象 */ /** 用户DAO */ @Autowired private UserDAO userDAO; /** 标识生成器 */ @Autowired private IdGenerator idGenerator; /** 定义依赖参数 */ /** 可以修改 */ @Value("${userService.canModify}") private Boolean canModify; /** * 保存用户 * * @param userSave 用户保存 * @return 用户标识 */ public Long saveUser(UserVO userSave) { // 获取用户标识 Long userId = userDAO.getIdByName(userSave.getName()); // 根据存在处理 // 根据存在处理: 不存在则创建 if (Objects.isNull(userId)) { userId = idGenerator.next(); UserDO userCreate = new UserDO(); userCreate.setId(userId); userCreate.setName(userSave.getName()); userCreate.setDescription(userSave.getDescription()); userDAO.create(userCreate); } // 根据存在处理: 已存在可修改 else if (Boolean.TRUE.equals(canModify)) { UserDO userModify = new UserDO(); userModify.setId(userId); userModify.setName(userSave.getName()); userModify.setDescription(userSave.getDescription()); userDAO.modify(userModify); } // 根据存在处理: 已存在禁修改 else { throw new UnsupportedOperationException("不支持修改"); } // 返回用户标识 return userId; } }
测试用例:
/** * 用户服务测试类 */ @RunWith(MockitoJUnitRunner.class) public class UserServiceTest { /** 定义静态常量 */ /** 资源路径 */ private static final String RESOURCE_PATH = "testUserService/"; /** 模拟依赖对象 */ /** 用户DAO */ @Mock private UserDAO userDAO; /** 标识生成器 */ @Mock private IdGenerator idGenerator; /** 定义测试对象 */ /** 用户服务 */ @InjectMocks private UserService userService; /** * 在测试之前 */ @Before public void beforeTest() { Whitebox.setInternalState(userService, "canModify", Boolean.TRUE); } /** * 测试: 保存用户-创建 */ @Test public void testSaveUserWithCreate() { // 模拟依赖方法 // 模拟依赖方法: userDAO.getIdByName Mockito.doReturn(null).when(userDAO).getIdByName(Mockito.anyString()); // 模拟依赖方法: idGenerator.next Long userId = 123L; Mockito.doReturn(userId).when(idGenerator).next(); // 调用测试方法 String path = RESOURCE_PATH + "testSaveUserWithCreate/"; String text = ResourceHelper.getResourceAsString(getClass(), path + "userSave.json"); UserSaveVO userSave = JSON.parseObject(text, UserSaveVO.class); Assert.assertEquals("用户标识不一致", userId, userService.saveUser(userSave)); // 验证依赖方法 // 验证依赖方法: userDAO.getIdByName Mockito.verify(userDAO).getIdByName(userSave.getName()); // 验证依赖方法: idGenerator.next Mockito.verify(idGenerator).next(); // 验证依赖方法: userDAO.create ArgumentCaptor<UserDO> userCreateCaptor = ArgumentCaptor.forClass(UserDO.class); Mockito.verify(userDAO).create(userCreateCaptor.capture()); text = ResourceHelper.getResourceAsString(getClass(), path + "userCreate.json"); Assert.assertEquals("用户创建不一致", text, JSON.toJSONString(userCreateCaptor.getValue())); // 验证依赖对象 Mockito.verifyNoMoreInteractions(userDAO, idGenerator); } /** * 测试: 保存用户-修改 */ @Test public void testSaveUserWithModify() { // 模拟依赖方法 // 模拟依赖方法: userDAO.getIdByName Long userId = 123L; Mockito.doReturn(userId).when(userDAO).getIdByName(Mockito.anyString()); // 调用测试方法 String path = RESOURCE_PATH + "testSaveUserWithModify/"; String text = ResourceHelper.getResourceAsString(getClass(), path + "userSave.json"); UserSaveVO userSave = JSON.parseObject(text, UserSaveVO.class); Assert.assertEquals("用户标识不一致", userId, userService.saveUser(userSave)); // 验证依赖方法 // 验证依赖方法: userDAO.getIdByName Mockito.verify(userDAO).getIdByName(userSave.getName()); // 验证依赖方法: userDAO.modify ArgumentCaptor<UserDO> userModifyCaptor = ArgumentCaptor.forClass(UserDO.class); Mockito.verify(userDAO).modify(userModifyCaptor.capture()); text = ResourceHelper.getResourceAsString(getClass(), path + "userModify.json"); Assert.assertEquals("用户修改不一致", text, JSON.toJSONString(userModifyCaptor.getValue())); // 验证依赖对象 Mockito.verifyNoMoreInteractions(userDAO, idGenerator); } /** * 测试: 保存用户-异常 */ @Test public void testSaveUserWithException() { // 注入依赖对象 Whitebox.setInternalState(userService, "canModify", Boolean.FALSE); // 模拟依赖方法 // 模拟依赖方法: userDAO.getIdByName Mockito.doReturn(123L).when(userDAO).getIdByName(Mockito.anyString()); // 调用测试方法 String path = RESOURCE_PATH + "testSaveUserWithException/"; String text = ResourceHelper.getResourceAsString(getClass(), path + "userSave.json"); UserSaveVO userSave = JSON.parseObject(text, UserSaveVO.class); UnsupportedOperationException exception = Assert.assertThrows("异常类型不一致", UnsupportedOperationException.class, () -> userService.saveUser(userSave)); Assert.assertEquals("异常消息不一致", "不支持修改", exception.getMessage()); // 验证依赖方法 // 验证依赖方法: userDAO.getIdByName Mockito.verify(userDAO).getIdByName(userSave.getName()); // 验证依赖对象 Mockito.verifyNoMoreInteractions(userDAO, idGenerator); } } 其中,加载的JSON资源文件内容如下: userSave.json: userCreate.json: userModify.json:
通过执行以上测试用例,可以看到对源代码进行了100%的行覆盖。
《Java单元测试实战》——编写方法:Java编程技巧之单元测试用例编写流程(3) https://developer.aliyun.com/article/1232418?groupCode=java