单测的标准:
语句覆盖率达到70%;核心模块的语句覆盖率和分支覆盖率都要达到100%。 --- 《阿里巴巴Java开发手册》
单测覆盖度分级参考
Level1:正常流程可用,即一个函数在输入正确的参数时,会有正确的输出
Level2:异常流程可抛出逻辑异常,即输入参数有误时,不能抛出系统异常,而是用自己定义的逻辑异常通知上层调用代码其错误之处
Level3:极端情况和边界数据可用,对输入参数的边界情况也要单独测试,确保输出是正确有效的
Level4:所有分支、循环的逻辑走通,不能有任何流程是测试不到的
Level5:输出数据的所有字段验证,对有复杂数据结构的输出,确保每个字段都是正确的
60%左右的单测覆盖率可以非常轻松达到,但达到95%以上的覆盖率,需要覆盖各种代码分支和异常情况等,甚至是配置和bean的初始化方法,所投入的时间非常巨大,但边际效应递减。我想测试toString, getter/setter这样的方法也没有意义。多少合适,我认为没有一个固定的标准。高代码覆盖率百分比不表示成功,也不意味着高代码质量。该舍弃测试的部分就大胆的ignore掉。
1、隐藏的测试边界值
2、不要在springboot测试中使用@Transactional以及操作真实数据库
3、单测里时间相关的内容
笔者曾经在工作中遇到过一个极端case,一个CI平时都正常运行,有一次深夜发布, CI跑不过,后来经过第二天check才发现有前人在单测中取了当前时间,在业务逻辑中含有夜间逻辑(夜间消息不发),导致了CI无法通过。那么时间在单测中要如何处理呢?
在使用Mockito时,可以使用mock(Date.class)来模拟日期对象,然后使用when(date.getTime()).thenReturn(time)来设置日期对象的时间。
如果你使用了calendar.getInstance(),如何获取当前时间?Calendar.getInstance()是static方法,无法通过Mockito进行mock。需要引入powerMock,或者升级到mockito 4.x才能支持:
final类,static类等的单元测试
如第3点提到的calendar的例子,static类的mock需要mockito4.x的版本。否则就要引入powermock,powermock不兼容mockito3.x版本,不兼容mockito 4.x版本。由于老的应用引入了非常多的mockito3.x的版本,直接使用mockito4.x对final和static类进行mock需要排包。实践中看,JUnit、Mockito、Powermock三者之间的版本号有兼容性问题,可能会出现java.lang.NoSuchMethodError,需要根据实际的情况选择版本进行mock。
但是在新项目立项的时候,要确定好使用的mockito和junit版本,是否引入powermock等框架,确保环境稳定可用。老项目建议不要大规模改动mockito和powermock的版本,容易排包排到怀疑人生。
总结一下什么时候使用容器:
// 1. 使用PowerMockRunner
@RunWith(PowerMockRunner.class)
// 2.使用PandoraBootRunner, 启动pandora,使用tair,metaq等
@RunWith(PandoraBootRunner.class)
// 3. springboot启动,加入context上下文,可以直接获取bean
@SpringBootTest(classes = {TestApplication.class})
不要为了覆盖率测没意义的代码
比如toString,比如getter,setter,都是机器生成的代码,单测没意义。如果是为了整体测试覆盖率的提高,那么请在CI中排掉这部分包:
如何测试void方法
如果void方法内部造成了数据库的变更,比如insertPlan(Plan plan),并通过H2操作过数据库,那么可以验证数据库的条数变化等,校验void方法的正确性。
如果void方法调用了函数,可以通过verify验证方法得到调用次数:
userService.updateName(1L,"qiushuo");
verify(mockedUserRepository, times(1)).updateName(1L,"qiushuo");
如果void方法可能会造成抛出异常。
可以通过dothrow来 mock方法抛出的异常:
@Test(expected = InvalidParamException.class)
public void testUpdateNameThrowExceptionWhenIdNull() {
doThrow(new InvalidParamException())
.when(mockedUserRepository).updateName(null,anyString();
userService.updateName(null,"qiushuo");
}
使用 squaretest testme junit5 + Mockito
IDEA 安装 squaretest testme 插件
配置模板后 在你想要的生的单测的类 右键生成 test类