自动化工具 测试开发之路 ---- 代码覆盖率 (EMMA)

孙高飞 · June 20, 2016 · Last by 刘扬 replied at June 01, 2017 · 3401 hits

前言

今天在家翻了翻以前的文档,偶然看到了以前用 emma 统计代码覆盖率时总结的一些东西。便突然有了兴致来讨论一下。相信大家都知道有 code coverage 这东西,也研究过。 但大部分公司基本都没有使用 code coverage 或者 code coverage 变成了没什么卵用的 KPI 产物。我总结了一下如果你想最大的发挥 code coverage 这种策略的威力,需要做的几个比较重要的点。

理解 code coverage 能做什么

再讲这些点之前,我们讨论一下 code coverage 能带来什么好处吧。

  1. 首先,它是我们质量保障中重要的一环。别指望单凭它就能证明你的产品没问题,但是它确实是一个重要的指标。它告诉我们,哪些逻辑被执行过了,哪些逻辑没有被执行过。通过分析报告,我们可以补充测试用例,可以去测试那些我们实现没有想到的路径。众所周知,未测试的代码在线上是危险的。
  2. 然后,它可以帮助我们定位 bug 路径。因为它清楚的告诉你,执行一个操作的代码走向。如果你看过我的另一篇帖子论自动化测试脚本的质量与效率。你可以发现 code coverage 配合被测功能的隔离 绝对是定位 bug 的利器
  3. 再然后,可以说它是我们发布之前最后的一关了。虽然即使 100% 的覆盖率都不能保证你的产品是没问题的,但起码能告诉你,你都测试了哪些逻辑。让你的心里有底。一些不错的团队都把覆盖率列为一项指标。我们说当我们的自动化测试的覆盖率比较高的时候,我们的产品就有了不错的自测性。有人称之为产品的保护伞。尤其是非互联网行业,例如在下这个 to B 业务为主的公司。没办法去搞什么众测,灰度和监控的。在产品进入客户公司的场地内之前,就必须要保证产品的质量。所以代码覆盖率,是一个很重要的指标。
  4. 最后,通过分析代码覆盖率报告,我们可以优化代码的质量,当然这部分工作一般都是开发做的。例如找到一些 dead code,废弃的功能等等。可叹的是几乎没有开发有这个闲工夫去搞这些

一些提高效率的点

高度自动化

想搞代码覆盖率,第一个前提是你的项目有能力用自动化的方式把覆盖率提高到一个量级。额,这个应该是个共识吧,除非你人力多到没地方用了。

单元测试

为了实现高覆盖率,必须要有单元测试。实际上,提升覆盖率的主力就是在单元测试这边。如果项目中的开发人员没有编写单元测试的习惯的话,几乎是注定了在覆盖率方面有很大的缺陷了。从实现成本上看,单元测试的 mock 技术是提高覆盖率的不二利器。想只靠测试人员在接口和 UI 级别将覆盖率提高到一定量级是需要极其庞大的成本的。而且有很多逻辑在测试环境中是根本覆盖不了的,所以我们才需要 mock。《Google 软件测试之道》一书中推荐的单元测试的覆盖率在 50%~70% 之间,可惜实际上我没听过几个国内的公司能再单元测试上把覆盖率提高到这个份上

测试人员要熟悉产品代码

导致代码覆盖率变成 KPI 产物的最大一个原因就是测试人员不熟悉产品代码,甚至根本看不懂覆盖率报告。这样根本无法根据报告补充 case,定位 bug。 代码覆盖率也就变成了摆设。

EMMA 的使用

java 中比较流行的代码覆盖率工具有 EMMA,Cobertura,jacoco 等。其实以现在情况来看,使用 jacoco 的人群是比较多的,有点大势所趋的感觉。本来以前用 EMMA 的人很多,但是开发这个工具的坑爹团队自从 2005 年以后就再也没更新过了。可以理解为 EMMA 已经是一个 dead project。但是我从以前就再用 EMMA,所以也一直没换,反正能满足需求就行了。至于到底哪个工具更高大上,我倒不是很在意。不过我还是建议刚搞代码覆盖率的同学直接去搞 jacoco 吧。毕竟 EMMA 都停止更新了,以后肯定会被淘汰的。不过其实所有代码覆盖率的原理都是差不多的,我们说一下 EMMA 的使用也并无不可,而且说实话 EMMA 使用起来确实太简单了,新手首选。

与 maven 集成

java 项目大多都是用 maven 管理的,如果我们想统计单元测试的覆盖率的话,通过 emma 与 maven 集成是最简单不过的。不像 jacoco 那么麻烦,配置 emma 十分简单。只需要添加如下依赖:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>emma-maven-plugin</artifactId>
    <version>1.0-alpha-3</version>
    <inherited>true</inherited>
</plugin>

然后运行命令:mvn emma:emma。 之后你就可以看 report 了

与 jenkins 集成

如果你使用 jenkins 作为 CI 的工具的话,其实就更简单了。你都不用再 pom 文件中增加依赖,安装好 EMMA 的插件以后,直接运行上面的例子的命令就好了。

插桩

上面介绍的都只能统计项目本身的测试,也就是在工程中的 src/test/java 包下面的测试脚本。如果是我们的接口测试,UI 测试呢?我们怎么做才能统计代码覆盖率呢?这就需要一些手段了。

  • 首先,你需要从官网中下载 emma.jar 到你的测试环境中,然后 copy 到你的 jre 的 ext 文件夹中。这样就是扩展了 java 命令,以后你就可以直接以 java emma 的形式执行操作了。
  • 然后你需要对被测的包进行插桩。然后 emma 会开启一个服务,默认端口 47653。这个服务就会监控被测的工程了。插桩的例子如下。具体的命令参数大家参照官网就好。
java emma instr -m overwrite -cp simba-1.0.jar -out coverage.em
  • 最后就是收集报告了。这里需要两个命令,一个是收集数据,一个是生成报告。例子如下。着重说一下几个参数。-sp 是你源代码的路径,这样 emma 才能获取代码信息展示更详细的报告。-in 是生成报告需要的元数据信息。是在插桩和收集数据生成的中间文件。-r 是 report 的格式。这几个参数是常用的。
java emma ctl -connect localhost:47653 -command coverage.get,coverage.ec
java emma report -r html -Dreport.out.encoding=utf-8 -sp /opt/web/simba/src/main/java -in coverage.em,coverage.ec

只要你不删除插桩和收集数据所产生的元数据文件的话。你都可以累计的生成报告。还有一个 merge 模式可以合并报告,详细的东西大家可以去官网看一下。EMMA 的好处就是使用简单。最后我发一个生成的报告的图吧。

总结

  • 即使达到 100% 的覆盖率也不能保障你的产品质量。
  • 但是覆盖率过低的产品的质量一定是不能被保障的。
  • 真正重视代码覆盖率,把它加入到质量保证的流程中。否则很容易变成摆设
  • 提高测试人员的代码水平,熟悉产品代码。否则也容易变成摆设
  • 不要妄图做到 100%,那不可能。
  • 保持一个覆盖率的平衡:单元测试 50%~70%,接口 20%~40%,UI10%。
  • 尽可能劝说开发人员写单元测试。越是底层的测试,实现覆盖的成本越低。妄图在测试人员这边达到高覆盖率几乎是不可能的。
  • 如果没有单元测试。尽量在接口测试中提高覆盖率

OK 就说这么多吧。想把代码覆盖率利用起来真的很难。因为遇到的都是非技术性原因。劝说开发写单元测试,劝说老大把覆盖率加入到质量流程中,劝说产品和其他 QA 关注覆盖率等等。这些哪一个都不容易。即便过了这几关你还要为了提高覆盖率做很大的努力。毕竟项目不会再一开始就开展自动化测试。等自动化测试人员加入到项目中的时候,没准已经遗留了成百上千的接口。这个技术债可能需要以年为单位去还。大家努力吧。

补充一个坑的解决方案

EMMA 有一个坑就是在 jdk1.6 以上的版本有可能出现一个问题,插桩之后运行会出现一个 classformat 异常。提示你参数数量不正确,大概是这个意思吧。其实这是 jdk 在 1.7 以后使用的验证器不一样了。而 EMMA 这个坑爹货太久没有更新了,根本没 cover 到新版本的 JDK。所以需要我们在启动 jvm 的时候增加一个参数。-XX:-UseSplitVerifier。这样就可以了。这个坑当时着实坑了我俩小时。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 22 条回复 时间 点赞

jacoco 就是 emma 的团队开发的 他们已经转向 jacoco 了

嗯用 emma 几乎没有了。都是 jacoco 了

#2 楼 @monkey 为什么我一用手机看你头像就是个二次元的妹子。。。电脑上还是原来的那头像

#1 楼 @seveniruby 恩。这玩意我用的确实过时了。一直懒得弄新的。觉得暂时够用了

#1 楼 @seveniruby 现在 jacoco 都出什么新功能了么

#5 楼 @ycwdaaaa 有赞的工程师做的不错. 他们最近的一篇文章讲解的很详细. 你可以看看

学习了

注入 war 包注入了几次都是 EMMA: [0 class(es) instrumented, 0 resource(s) copied]
不知道是什么问题

用 EMMA 还是挺麻烦的,需要手动插桩、收集数据、生成报告;多个组件同时运行还需要修改端口;增量代码覆盖也麻烦。不知道 LZ 有没有好的方式。

孙高飞 #11 · June 21, 2016 Author

#7 楼 @seveniruby 我去搜一搜吧

孙高飞 #12 · June 21, 2016 Author

#10 楼 @songguo 求助一下 jacoco 吧,我以后也会换 jacoco 的

孙高飞 #13 · June 21, 2016 Author

#9 楼 @hayabusa 遇见过。你重复插桩就会出现这个问题。或者路径写错了

孙高飞 #14 · June 21, 2016 Author

#10 楼 @songguo 用 EMMA,你是怎么解决增量代码覆盖的呢?就是假如代码更新了,你更新代码之后,怎么和之前的覆盖率整合呢?

#13 楼 @ycwdaaaa 应该不是路径问题,而且我也是首次插桩,怎么配置日志等级呢?我想看下什么报错

孙高飞 #16 · June 21, 2016 Author

#15 楼 @hayabusa 日志等级我也忘了,你去官网看看吧

孙高飞 #17 · June 21, 2016 Author

#7 楼 @seveniruby 能再请教一下么哈,现在 jacoco 相对于 emma 有什么大的优势? 我现在是做TOB业务的,没有移动端的软件。我查了一些资料都是说再安卓下很强大。

楼主,能请教下 war 包的打桩问题么?classes 和 jar 好像都没问题,但是 war 包的时候就 EMMA: [0 class(es) instrumented, 0 resource(s) copied] 了

#17 楼 @ycwdaaaa 传统使用没区别 多了几个新功能 比如在线动态插桩 支持实时 dump 覆盖率数据

#14 楼 @ycwdaaaa 其实增量实施也不多,挺麻烦。有一个思路是通过 SVN 的 diff 获取到变更代码集,然后有针对性打桩,获取到增量代码覆盖度。至于合并到之前的覆盖度结果里面,没有啥思路了,em 文件都变了。

孙高飞 #21 · June 28, 2016 Author

#19 楼 @seveniruby 换成 jacoco 了,就是这个报告怎么感觉有点别扭? 你们用的时候 summary 这里也是这样的么?

Kenneth 回复

你好,你使用的 jacoco 吗??我现在遇到了点问题,怎么给工程插桩呢

ABEE ycwdaaaa (孙高飞) 在 TesterHome 的发帖整理 中提及了此贴 12 Jan 13:47
需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up