前言
开发流程
这也太敏捷了
你的单元测试呢?
测了几个功能?
代码覆盖率多少?
哦,你说这些啊,我从来不写单元测试!
惊!你这单元测试的姿势都不对,就和打王者一样,同样是玩游戏,有人躺着,有人跪着……
来,赶紧过来跟着我看看单元测试!
1 项目分层
一般开发过程中,都是要对项目进行分层的,先来看看阿里巴巴 Java 开发手册
中,是如何对项目进行分层的?
Java 开发手册
当然实际工作中不可能是这样的,再来看看我一般使用的分层:
差不多这样吧!
2 单元测试
至于单元测试,一般情况下,是很少写。(除非无奈,一般人都不写吧!)
不会吧!不会吧!还有人写单元测试,单元测试不就是为了代码扫描的时候通过一下嘛?
其实,单元测试是有要求的!
在 IDEA 中 clean install 时,也会执行 maven 的 test 插件,运行一遍所有的 junit。
当然,如果希望执行 test 可以点击
或者执行命令:
忽略掉测试即可。
也有其他的方法忽略测试,比如配置 <skipTests>true</skipTests>
或 <maven.test.skip>true</maven.test.skip>
。
总之,单元测试是会在打包的时候自动执行,也可以忽略。并且单元测试要允许重复执行。
不能说我今天执行成功,第二天执行失败。
具体可以在阿里巴巴 Java 开发手册
单元测试章节已经说明很多了。
至此,都是为了说明一个道理,单元测试很重要!
下面来看看单元测试的使用姿势!
3 单元测试的使用
既然对项目进行了分层,那肯定要每一层都要测试到。所以就一起看看我最近学到的单元测试的新知识!
IDEA 快捷创建单元测试
在开始之前,先说下 IDEA 的快捷键,有助于提高开发效率。
⌥ ⏎ (Option Enter)
⌘ N
快捷创建 Junit 测试类。
姿势一:Dao 层测试
需要注解:
@Transactional
@Rollback
@Sql
比如呢?我想测试插入用户。
代码语言:javascript复制@Slf4j
@Rollback
@Transactional
@RunWith(SpringRunner.class)
@SpringBootTest(classes = WebApplication.class)
public class UserMapperTest {
@Autowired
private UserInfoMapper userInfoMapper;
@Test
@Sql(statements = "delete from user_info")
public void insertUser() throws CustomException {
// 创建 userInfo 对象, 都是伪代码!
UserInfo userInfo = new UserInfo();
userInfo.setUserId("1000001");
userInfo.setUserName("liuzhihang");
userInfoMapper.insertUserInfo(userInfo);
UserInfo record = userInfoMapper.selectById("1000001");
Assert.assertNotNull(record);
}
}
@Sql(statements = "delete from user_info")
注解,保证 junit 执行之前,把表清了;- 先插入再查询,Assert 断言结果是否 OK;
@Rollback
、@Transactional
保证 junit 执行结束回滚。
姿势二:Service 层测试
需要注解
@MockBean
@Slf4j
@Rollback
@SpringBootTest(classes = WebApplication.class)
public class UserServiceTest {
@MockBean
private UserInfoMapper userInfoMapper;
@Autowired
private UserInfoService userInfoService;
@Test
public void insertUser() throws CustomException {
// 创建 userInfo 对象, 都是伪代码!
UserInfo userInfo = new UserInfo();
userInfo.setUserId("1000001");
userInfo.setUserName("liuzhihang");
Mockito.when(userInfoMapper.selectById("1000001")).thenReturn(userInfo);
UserInfoRespDTO respDTO = userInfoService.queryUserInfo("1000001");
Assert.assertNotNull(respDTO);
}
}
意思到了就行,细节不重要。
核心姿势:
@MockBean
Mockito.when().thenReturn();
这样可以在 service 方法中调用到其他组件的某个方法的时候,mock 一个返回数据。比如:
- Mock Mapper 层的返回结果;
- Mock 远端的返回结果。
Mock 远程调用时,需要使用门面模式把远程 Dubbo 接口或者 Http 接口进行包装,然后 mock 自己的门面即可。
姿势三:Controller 层测试
这个就简单了,直接项目起起来,然后一个 Postman,一切结束。
其实,并不然。
需要注解:
@WebMvcTest
@MockBean
需要注入对象:
MockMvc
@WebMvcTest(UserInfoController.class)
public class UserInfoControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserInfoService userInfoService;
@Test
public void queryUserInfo() throws CustomException {
// 创建 userInfo 对象, 都是伪代码!
UserInfoDTO userInfoDTO = new UserInfoDTO();
userInfoDTO.setUserId("1000001");
userInfoDTO.setUserName("liuzhihang");
Mockito.when(userInfoService.queryUserInfo("1000001")).thenReturn(userInfoDTO);
mockMvc.perform(post("/user/queryUserInfo")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(JSON.toJSONString(reqVO)))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value("00000"))
.andReturn()
.getResponse()
.getContentAsString();
}
}
核心姿势:
@MockBean
Mockito.when().thenReturn();
MockMvc
mockMvc.perform……
调用 service 时,mock 一份 service 返回的 DTO,然后使用 mockMvc 对象请求接口并校验返回。
姿势四:使用断言
除了上面介绍的注解之外,还需要注意 Assert
断言的使用。
一般情况下默认的 Assert
就可以满足使用,当然复杂情况的断言可以使用 Mockito
框架提供的断言。
具体姿势,就看看官方文档吧!
4 总结
本文简单介绍了为什么要使用 junit,以及如何使用 junit 对工程的各个分层进行测试。
心动不如行动。
赶紧去试试吧!
- <End /> -