背景
为了打通测试和开发界限,更好的保证产品质量,我们决定在黑盒测试的场景下分析代码的覆盖率情况,从而分析我们现有的case的完备程度,启发一些没有考虑到的场景。
好吧,以上为自动化组QA Manager的说辞,真实情况是他跑过来问我们能不能帮他们想想看能不能统计出他们自动化测试的代码覆盖率是多少。乍一听,有点蒙圈,意思黑盒测试还要看代码覆盖率的么,那我们写了这么多的白盒测试的UT是不是有点无地自容?虽然听起来启发一些测试场景还挺有道理的,但是感觉实际操作起来,个人觉得可能不一定那么理想。不过没关系!作为服务端的我们,就是要做好服务工作嘛!于是开始倒腾了一下,怎么能给黑盒测试统计处代码覆盖率。
原理
大概的原理如下:
- 以Jacoco Agent作为Java Option参数启动工程
- Jacoco Agent以tcp server的方式 dump出 jacoco.exec 数据文件
- 通过第三方工具,解析exec文件,从而得到覆盖率结果
当然,说的这么抽象的话,可能有些地方还是比较模糊的,我们就再更加具体一点。首先,我们知道Jacoco有两种处理方式on-the-fly和offline,一些更具体的内容,可以参考JAVA代码覆盖率工具JaCoCo-原理篇。简单点来讲,on-the-fly就是可以运行中进行埋点和数据采集,而offline则需要预先埋点。由于我们只是在测试环境进行一个数据收集而且能控制到环境中的Java Options,所以采用了在线的方式来统计结果,这样就可以拿到实时的数据而且无需中途停止或者重启服务器。
其次,在线的方式会使用到JVM本身提供的Intrumentation的特性,这种方式的主要原理就是通过调用JVM提供的接口,将所有需要load到虚拟机中的class进行自己的包装和修饰(具体在jacoco中就是行为埋点),可以理解为最终我们得到了一个全新的包含了埋点的class文件。具体可以参考一下Intrumentation相关的原理,其实其适用场景还有很多,如谈谈Java Intrumentation和相关应用
最后,得益于Jacoco的agent除了提供给我们普通file的方式外,还允许我们通过tcp server的方式,通过tcp连接来发送dump命令,并下载到相应的exec文件。这样我们就能远程不停机的情况下得到最新的覆盖率统计情况,从而解析得到报表。
实施
具体的实现步骤如下,部分需要根据需求进行取舍和更换:
- 下载jacoco的agent,并且在启动时以options的方式加入到启动参数中(注意使用tcpserver的方式启动):
java -jar xxx.jar -javaagent:$JACOCO_HOME/jacocoagent.jar=includes=com.xxx.yyy.*,output=tcpserver,port=8044,address=127.0.0.1 -Xverify:none
这里主要是由于这个项目采用了jar直接运行的方式,如果采用的是war报部署tomcat之类的方式,可以采用在tomcat中加入angent的方式来运行,如Jacoco远程统计tomcat服务的代码覆盖率。(在如果没有办法加入javaagent的话,就只能选择离线的方式来获取dump,而走不通这种方案)
- dump覆盖率文件,并生成报告。这里有很多中方式可以处理,比如:
- 通过eclipse的插件来直接获取和分析 Jacoco覆盖率使用总结
- 通过ant编写xml来获取
- 编写tcp连接来获取和生成(自己实现起来会比较复杂)
我们一开始实验了一下eclipse的emma插件,其实效果还是不错的,作为开发来说其实也挺方便的。但是考虑到我们实际面向是QA的同学,而且本身项目也是通过ant来进行管理的,所以最终选择了ant来进行处理,而且这种处理也利于后面我们集成到Jenkins中,加入到持续集成中。
xml的内容如下,且后续使用命令ant b_cover既可直接生成最终的html。其中reset和append参数需要特别注意一下,两者共同使用决定了你的覆盖率是否包含了上次已经被覆盖到的代码(代码中的$变量需要自己预先定义,这里没有写出来)
<target name="b_cover" xmlns:jacoco="antlib:org.jacoco.ant" >
<jacoco:dump address="127.0.0.1" port="8044" reset="true" destfile="${report.dir}/remote/jacoco.exec" append="false" />
<jacoco:report>
<executiondata>
<file file="${report.dir}/remote/jacoco.exec"/>
</executiondata>
<structure name="JaCoCo Report">
<classfiles>
<fileset dir="${classes}">
<exclude name="**/*Test*.class" />
<include name="**/com/xxx/yyy/*.class"/>
</fileset>
<fileset dir="${test-classes}">
<exclude name="**/*Test*.class" />
</fileset>
</classfiles>
<sourcefiles encoding="UTF-8">
<fileset dir="${src.dir}"/>
<fileset dir="${test.dir}"/>
</sourcefiles>
</structure>
<html destdir="${report.dir}/remote" />
<csv destfile="${report.dir}/remote/report.csv" />
<xml destfile="${report.dir}/remote/report.xml" />
</jacoco:report>
</target>
结论
最终,通过Jacoco的tcpserver,以及ant脚本的方式,统计出了API自动化测试的覆盖率。结果比我们想象的还理想一点,大概75%不到一点。不过具体的结果还没有进行仔细的分析,所以这个覆盖率的水分如何,以及是否真的有用,还不太清楚。
从个人的看法来说,这个主意虽然看起来还是有那么点意思,但是以下几点还值得商榷:
- 首先如果从覆盖率来看测试结果,感觉限制了QA的思考方向(说白了就是开发把测试往沟里带的感觉,毕竟测试的一个重要考量就是从非开发的角度去思考一些场景和问题)
- 其次感觉和白盒测试的工作有了一定的重叠,究竟这样能带来多大的好处或者说性价比,结论还有带商榷。
- 再者覆盖率是否真的能体现代码质量,对于做了非常多UT的我们,其实应该明白,答案是否定的。虽然覆盖率的代码质量多半不好,但是覆盖率高的代码质量好不好还真是一个未知数。
- 对测试同学的要求突然就高了很多,现实情况下很难达到。因为如果测试能看懂我们的代码的话其实开发起来也是完全ok的(TDD表示:先有鸡还是先有蛋)