我们都知道,持续交付的流程包括如下几个步骤,
开发 -> 单元测试(覆盖率) -> 合并代码 -> 集成测试(自动化回归) -> 上线(持续部署)
之前做运维的时候,我更多关注的是从集成测试到上线的部分,做持续部署。现在作为研发,更多关注的是从开发到集成测试的部分。有效的测试可以极大避免bug出现在生产环境。而在测试中最底层,最重要的就是单测。
一、单元测试
如何界定测试的单元? 我理解单元基本可以等同于方法,所以在统计单测覆盖率的时候,会有单测类覆盖率、方法覆盖率、行覆盖率等。当然,单测也可以是一组方法,甚至涉及到多个类的多个方法,这都不强制规定。
单测的最主要目的是用来发现测试未覆盖的代码。单测覆盖率高不能说明代码质量高,但覆盖率低一定说明代码风险较大。一般而言,方法覆盖率至少到80%,除一些非常简单的方法外。
在项目开发比较紧张的时候,或要快速出原型的时候,可以不写单测,但记住,这一定属于技术债务,需要及时补上,否则在后续重构、升级的时候,很可能引入其他问题,把代码改坏。
1.1 单元测试的基本套路
一个单元测试的核心有4步,准备数据 -> 执行测试逻辑 -> 断言 -> 清理数据
数据准备与清理,可能会涉及到的注解有@BeforeClass
, @AfterClass
, @Before
, @After
,说明下@BeforeClass
和@Before
的区别,另外一对类似。
-
@BeforeClass
只执行一次,必须标注在类方法上(static void method)。 -
@Before
在每个测试方法前都会执行,标注在成员方法上。
1.2 如何设计单元测试
单元测试要尽量覆盖代码分支,最好为每一个场景,每一个分支写一个单测。并且需要模拟一些异常场景,比如调用某一个外部服务出现异常了,你期望的结果是什么,代码执行是否符合预期。
但现实很骨感,往往让“为每一种场景写单测”变得不现实。比如在写的时候会发现有各种问题,比如,怎么能模拟到某一个异常分支,或者怎么模拟一个异常抛出来,搞出脏数据怎么办?等等。
这时就可以用一些mock的方法,把一些危险/比较重的操作给模拟掉。将测试的重点放到业务逻辑上。
1.3 单测的命名
对于命名,我向来比较纠结。起一个好的名字可以很好地帮助别人理解你的意图。
对于单测,我自己的诉求是,首先我想知道自己测的是哪个方法,其次我想知道在什么条件下,最后我想知道出现的结果是否符合预期。
所以,我个人比较倾向于<method>_should_<expected>_when_<condition>
这种命名方式。看起来比较清晰。
但如果你重命名了method,那就需要手动改下单测了.. 没有一个IDE的refactor可以直接把单测的相关方法中method也改掉..