1 概述
1.1 编写目的
好的代码规范能够让代码质量大大提升,好的单元测试,可以让代码逻辑更加严谨,符合实际业务需要。写好单元测试,有以下优点:
Ø 提高代码正确性
l 流程判读符合预期,按照步骤运行,逻辑正确。
l 执行结果符合预期,代码执行后,结果正确。
l 异常输出符合预期,执行异常或者错误,超越程序边界,保护自身。
l 代码质量符合预期,效率,响应时间,资源消耗等。
Ø 发现设计问题
l 代码可测性差
l 方法封装不合理
l 流程不合理
l 设计漏洞等
Ø 提升代码可读性
l 易写单测的方法一定是简单好理解的,可读性是高的,反之难写的单测代码是复杂的,可读性差的。
Ø 顺便微重构
l 如设计不合理可微重构,保证代码的可读性以及健壮性。
Ø 提升开发人员自信心
l 经过单元测试,能让程序员对自己的代码质量更有信心,对实现方式记忆更深。
Ø 启动速度,提升效率
l 不用重复启动Pandora容器,浪费大量时间在容器启动上,方便逻辑验证。
Ø 场景保存(多场景)
l 在HSF控制台中只能保存一套参数,而单测可保存多套参数,覆盖各个场景,多条分支,就是一个个测试用例。
Ø CodeReview时作为重点CR的地方
Ø 好的单测可作为指导文档,方便使用者使用及阅读
1.2 名词解释
单元测试(unit testing),指由开发人员对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。
2 单元测试规范
2.1 单元测试原则
Ø 单测对实现类进行测试而非接口
Ø 单测应该是重复执行的(无状态),不能受到外界环境的影响。
l 单测通常会被放到持续集成中,每次有代码 check in 时单元测试都会被执行
Ø 核心业务、核心应用、核心模块的增量代码确保单测通过
l 新增代码及时补充单元测试,如果新增代码影响了原有单元测试,请及时修正。
Ø 单测期望目标:
l 语句覆盖率达到 85%;核心模块的语句覆盖率和分支覆盖率都要达到 100%
Ø 单测要覆盖到正常分支和异常分支
2.2 单元测试编写规范
Ø 目录结构
l 单元测试必须放在工程目录:src/test/java下,不允许写在业务代码目录下
l 单元测试类文件子目录应与类文件目录保持一致
Ø 推荐命名规则
l 类名:【被测类】Test,举例 JobStatisticMetricServiceImplTest
l 方法名:test【被测方法】_【被测场景】,举例 testListHolidays testListHolidays_JobDayMapperReturnsNoItems
Ø 步骤
一个完整的单元测试用例应该包含如下三个部分:
Configure/ Setup:准备当前测试场景的前置条件
Run the test:执行被测试的方法
Verify the results:验证结果
示例代码如下:
@Test void testListHolidays() { // Setup final JobDay jobDay = new JobDay(); jobDay.setDay("day"); jobDay.setCode("code"); final List<JobDay> expectedResult = List.of(jobDay); // Configure JobDayMapper.listByExample(...). final JobDay jobDay1 = new JobDay(); jobDay1.setDay("day"); jobDay1.setCode("code"); final List<JobDay> jobDays = List.of(jobDay1); when(mockJobDayMapper.listByExample(any(JobDayExample.class))).thenReturn(jobDays); // Run the test final List<JobDay> result = jobStatisticMetricServiceImplUnderTest.listHolidays("startDate", "endDate"); // Verify the results assertThat(result).isEqualTo(expectedResult); } @Test void testListHolidays_JobDayMapperReturnsNoItems() { // Setup when(mockJobDayMapper.listByExample(any(JobDayExample.class))).thenReturn(Collections.emptyList()); // Run the test final List<JobDay> result = jobStatisticMetricServiceImplUnderTest.listHolidays("startDate", "endDate"); // Verify the results assertThat(result).isEqualTo(Collections.emptyList()); }
2.3 常用方法实现
Ø 相关注解
推荐采用以下注解实现单元测试:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
Ø 相关方法
推荐采用Mockito,进行编写Java的单元测试。
常用方法:
when(T methodCall) thenReturn(T value):当参数方法执行后,返回后边结果
assertThat(List actual). isEqualTo(Object expected):验证结果等于后边期望值
2.4 快速生成实现
单元测试作为开发人员的一项自测手段,往往会增加开发人员的工作量,为了减少重复的工作,可以采用快速生成框架,快速生成符合规范的单元测试实现。
Ø IDE插件
IntelliJ IDEA 推荐使用Squaretest,相关使用说明,可以自行学习。