如何写测试 六
在这一章之中,我们将介绍在覆盖率之中,包含哪几种度量的指标。以及该如何设置测试覆盖率。
首先对于测试覆盖率来说,往往有多种更具体的测试指标来度量。
比如:
函数覆盖率,代码中定义的函数有多少得到了调用。
语句覆盖率,代码中有多少语句得到了执行。
分支覆盖率,代码中存在多少分支得到了执行。
条件覆盖率,每个布尔表达式中的子表达时是否都存在true和false的执行。
行覆盖率,代码中有多少行得到了测试。
上面条件覆盖率和分支覆盖率可能有些难以理解,条件覆盖率是值得在下面的布尔表达式之中if ((a || b) && c) {,每个子表达式的真假值。
而分支表达时,则是对if else 这类进行覆盖即可。
对于覆盖率的测试,我们可以以行覆盖率为例,如果我们有100行代码,其中执行测试之后就执行了80行,那么覆盖率就是0.8
而在上述的覆盖率的指标中,比如条件覆盖率,往往很难达标,所以在实际的生产应用的时候,可以去选取一个相对合理的指标。
并且我们还可以看下可以使用的覆盖率测试工具,这里我们以Java语言为例,说明其中常用的Jacoco工具。
在这个工具之中,可以对代码进行多指标多维度的进行查看。
一个指标作为一个纬度,对应的概念是counter,
那么不同的指标,就是不同的counter
这里我们可以选择使用哪些counter,以及选取的counter的最大值和最小值区间。就比如我们要求行的覆盖率达到80%,那么就可以配置为
counter: “LINE”, value: “COVEREDRATIO”, minimum: “0.8”
而且在JaCoCo之中,我们可以得多种类型的报告格式,比如XML,CSV,HTML等格式
就比如html格式文档,可以得到哪些测试的指标没有达标,并且可以看到哪些代码没有覆盖到。

那么在项目中,对于测试的实际集成,一般是放在自动化阶段进行检查,如果集成到自动化流程之中,一般情况下,将毫无影响。但如果一旦没有达到测试率,那么就会报错,得到错误提示。
在集成到项目的自动化测试之后,就需要考虑一点,就是该设置多少作为测试覆盖率的达标值。
这里可以考虑设置到100,如果实在打不到,在稍微降一点,至于为什么建议是100呢?
一方面,如果设置的低了,总会有人询问为什么不设置高一些,但如果直接设置100,那么就没有被质疑的可能性。
还有就是利用一个高的代码测试率,来保证代码在线上不会出一些低级错误。
那么如何去保证代码的测试率达到100%呢?
这可以结合之前所讲的,一方面我们要保证自己的代码有完全的测试保证,就是在设计一个功能的时候,设计之初,就考虑如何设计测试,在书写的代码的同时书写测试。这样我们就可以保证在小功能书写完毕的时候,就将测试书写完毕。
再其次是关于第三方的代码,如果有第三方的代码影响到测试覆盖率,应该将第三方的代码和自己的代码隔离开,这个隔离还是非常薄的一层,从而保证了我们的代码有很好的测试覆盖。
如果不进行抽取,必然达不到100%的测试覆盖率,就比如我们之前说过的序列化和反序列化的代码。这类依赖的代码,就可以分离出来,形成工具类。并且在测试覆盖的检查中进行排除。
当然这并不是偷懒的意思,关于这类工具类的代码,我们需要进行进行严格的代码审计,之后才能进行merge。
通过这类方法,我们可以先考虑将代码的测试覆盖率设置为100%,如果实在和代码开发周期有冲突,再考虑降低。
这里我们说了不同的代码覆盖率指标,说明了指标之外,我们还以Java作为语言,说明了如何进行测试。
再之后,我们说明了代码的测试覆盖率应该设置为多少,这里建议100,并说明了如何做到。如果实在达不到,再考虑降低
在说完了单元测试之后,我们可以来看看如何进行集成测试。
如果说每个单元其独立测试都能达到预期,那么在单元测试之后,就需要考虑集成测试来测试单元之间的协同。
不过在说如何去做之前,我们可以先看下集成测试的概念,集成测试到底测的是什么呢?
说白了,集成测试就是将不同的组件组合在一起,查看其是否可以很好的配合工作。
落到实际的代码中,比如REST服务,一般来说有三个组件,Controller,Service,Repository。
一般只需要三者集合在一起进行一次测试,而不需要两两进行一次测试。毕竟我们已经做好了每个组件的单独测试,只需要选择一条路径,然后将路径从头到尾走一遍就可以。
如果在代码中还有框架的集成。那么就需要考虑,将框架在进行集成测试的时候引入进来。就比如集成了Spring Boot,那么就可以将其在集成测试的时候引入进来。
但是在不同项目中,可能使用的框架是不同的。市面上提供的框架也是层出不穷。那么一个框架是否设计的好坏,很大一份取决于对测试的支持程度。
上面所说的,归根到底还是代码内部的集成测试,如果测试之中涉及到了外部资源的话,比如消息队列,比如外部数据库。这里我们拿之前的Todo保存来说事,我们之前的测试之中,是判断数据库层是否只有一条消息,那么如果反复测试中不去每次测试完成清理。会导致多条记录从而断言失败。
这里我们可以以MySQL为例,首先可以为测试准备一个单独的分库。
CREATE DATABASE todo_test;
然后保证每次测试完成之后,都能恢复到之前的状态。
利用这种方式,我们就可以去验证我们的Repository层,也就是数据访问层的代码实现。
不过除了数据库这种支持做的好的组件外,很多外部组件的支持都是不那么令人满意。就好比第三方服务器,很多都没有提供测试支持。对于这类情况,我们就要分析下,有没有什么替代方案。就比如我们可以利用mock服务器的方式,将第三方服务整体进行mock。从而完成这样的一个集成测试。
那么总结一下,对于集成测试来说,集成测试更加关注多组件之间的协同工作。
在除了多组件本身之外的系统,还需要考虑在使用框架的时候和框架的集成。
最后还有和外部组件的集成,主要的难点在于如何控制外部组件的状态。对于数据库还好,但是其他的第三方组件,可以考虑利用mock的方式模拟行为。