vivo 基于 JaCoCo 的测试覆盖率设计与实践

作者:vivo 互联网服务器团队- Xu Shen

本文主要介绍vivo内部研发平台使用JaCoCo实现测试覆盖率的实践,包括JaCoCo原理介绍以及在实践过程中遇到的新增代码覆盖率统计问题和频繁发布导致覆盖率丢失问题的解决办法。

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:82e5720a-5bee-46af-b9fa-f0e975020e95

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:b0ffef54-3530-4562-8dba-7df8fcef9969

1.1 在日常研发过程中,经常发现一些问题

  • 测试案例的设计凭经验,当研发一个新功能时,经常对测试场景估计不足,到上线后发现bug;
    [TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:20c4b027-44ed-4522-bf93-974190c5fc76
    [En]

    [TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:dc08d6ec-7148-4ab5-928e-2093359c6bd8

    [TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:6a0db6c7-2c4f-4cc6-a8a2-3506e2146be2

    [En]

    [TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:a3f43087-fc2d-4f9c-8ffd-420b8f938b9f

1.2. 有没有技术手段能够尽可能的避免上面的问题呢?

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:702bb7a0-bda5-437a-96b5-7bf74bedc84a

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:f5ffa0df-260b-4f8d-8be6-4e4740e41468

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:0a2a2e18-b477-42cc-9e3f-df33adbc3b81

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:4a969cd4-222b-43db-8e06-fe05adbe5f10

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:3df379f1-d6bb-47ab-b347-a92661139e5f

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:3cfd24be-1997-4c39-baa7-a9af5f0ecfef

  • 函数/方法覆盖率:函数/方法中有多少被调用到
  • 分支覆盖率:有多少控制结构的分支(例如if语句)被执行
  • 条件覆盖率:有多少布尔子表达式被测试为真值和假值
  • 行覆盖率:有多少行的源代码被测试过

1.3 在使用测试覆盖率的过程中,经常发现的场景

  • if/else语句中,if{}内的代码被覆盖到,else{}内的代码没有被覆盖到,可以得出部分分支场景没有测试到;
  • try/catch语句中,try{}内的代码被覆盖到,catch{}内的代码没有被覆盖到,可以得出异常场景没有测试到;
  • if (条件1 || 条件2 || 条件3)语句中,条件1被覆盖到,条件2和条件3没有被覆盖到,可以得出部分条件场景没有测试到;

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:2962e0a6-a6f4-408e-82d5-dcd8d5097e5c

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:f0b53174-5db0-4af1-958e-4406b722ac2a

二、JaCoCo在测试覆盖率场景中的使用

2.1 JaCoCo介绍

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:0b0da736-c4a4-409b-b5c9-a50cca6723f4

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:863d5f28-5870-4a18-92f1-36ea628ac7b0

C/C++→Gcov ,Java→JaCoCo,JavaScript→ Istanbul。

考虑到服务器端主要是Java语言,所以CICD平台优先使用JaCoCo来支持 Java 语言的代码覆盖率统计能力。

通过JaCoCo官网,我们可以看到JaCoCo的使命是为Java VM 的环境中的代码覆盖分析提供标准技术。重点是提供一个轻量级、灵活且有据可查的库,用于与各种构建和开发工具集成。

2.2 JaCoCo优点

  • JaCoCo支持指令(C0)、分支(C1)、行、方法、类和圈复杂度等多维度的覆盖分析;
  • 基于 Java 字节码,也可以在没有源文件的情况下工作;
    [TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:77320913-863a-4f97-b6d4-089fd6014f74
    [En]

    [TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:3d33cb1d-3695-4911-87fa-489bc324c1d9

  • 比较完整的API,很方便与其他工具进行集成;
  • 远程协议和 JMX 控制可在任何时间点从代理请求执行数据下载。

2.3 JaCoCo原理

主要来自于JaCoCo官方网站

JaCoCo支持几种不同的方法来收集覆盖信息,对于每种方法,由不同技术实现的,下图橙色路径部分是JaCoCo 推荐使用的方式,即通过On-The-Fly方式收集覆盖率信息:

vivo 基于 JaCoCo 的测试覆盖率设计与实践

通过上图我们知道,JaCoCo 是通过对Java字节码(Byte Code)插入探针的方式来收集覆盖率信息的,探针是可以插入现有指令之间的附加指令。它们不会改变方法的行为,但会记录它们已被执行的事实。

下面以一段简单的 程序为例进行说明:

vivo 基于 JaCoCo 的测试覆盖率设计与实践

这段代码经过Java编译以后转化为以下字节码:

vivo 基于 JaCoCo 的测试覆盖率设计与实践

因为Java 字节码指令的线性序列,控制流是通过条件或无条件指令实现跳转的,跳转目标在技术上是相对于目标指令的偏移量。这个跟大学学习的汇编指令的跳转方式类似,为了更好的可读性,使用符号标签 (L1,L2 ) 代替实际的指令地址。

vivo 基于 JaCoCo 的测试覆盖率设计与实践

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:541df2b5-70b7-46ed-a500-b766fda5c580

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:38fb1465-51b0-4245-901b-3b6a600b7ee1

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:ecd8931a-a5ba-474f-8815-27cef54ce83d

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:80f909b7-b371-43d4-b245-addbbb16a450

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:93a62c9f-f178-4f35-b07f-d7914ba8b06b

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:32838397-40bf-420b-93fa-8053dfa384c7

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:cd9c3776-e1fd-4f49-a9ba-350c55bf139f

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:2c0b0da8-be4c-4a47-be72-c68288ac9a82

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:1abaa488-48d1-44bd-8233-d34c55332980

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:0c02059a-c657-48de-9abd-b277c2b047dd

三、CICD平台关于测试覆盖率的解决方案

通过上面对JaCoCo原理的介绍,结合我们公司内部的研发流程,在CICD平台对代码覆盖率功能的设计如下:

vivo 基于 JaCoCo 的测试覆盖率设计与实践

从上面 CICD 平台对测试覆盖率的设计图,大概可以看出来,整个过程包含三个阶段

3.1 测试前

测试前由测试人员(开发人员/运维人员)在流水线上开启测试覆盖率功能,在流水线执行发布时,会在测试环境上下载JaCoCo Agent包,并在Java进程启动时配置JavaAgent参数;

在进程启动过程或启动之后,有class文件被加载时被Agent拦截,对class文件进行插桩处理,在必要的路径下插入探针(插入探针的原理在上一节已经介绍)。

3.2 测试中

在测试过程中,测试人员在测试环境执行测试案例(手动执行或自动化脚本),被调用到的代码会被探针记录下来,探针数据保存在Java进程的内存中。

3.3 测试后

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:5cd82681-d988-406e-88c0-9154599c3b55

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:f3aff964-7c23-4722-94c1-9b019eba6437

在测试结束后,CICD平台通过JaCoCo的API,手动/自动下载(dump)覆盖率数据,合并(merge)历史覆盖率数据,生成测试覆盖率报告;

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:fb65e1ba-2dab-4cf1-91f5-62fec088b4bd

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:7642bd90-2786-4ca7-b4bf-e57be31884b6

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:7fb03836-ad8f-4e0f-af00-004c73007e4c

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:38688ff8-90c3-4386-a46f-54f62a9ab93c

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:a0c86ddf-c223-40e6-8912-107f05eda456

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:21aab88f-450b-458f-85ca-a8bcd57d6574

4.1 在不同机器编译会导致classid不一致的问题

在实践过程中,经常遇到这样一个问题,用户反馈并确认案例已经正常执行,但是生成的报告显示未覆盖,经过调查发现在测试环境中的class和生成报告时的class不一致导致的。

在 JaCoCo内部,覆盖率数据是以classid作为key来存储的,classid是根据class的字节码hash算法得出来的,看JaCoCo源码中关于classid的算法如下:

vivo 基于 JaCoCo 的测试覆盖率设计与实践

出现不一致的情况包括:

  • 发布时编译的机器和生成报告的机器环境上有差异,比如操作系统版本、JDK版本等,导致编译的class不一致;
  • 发布时编译的代码版本与生成报告时的代码版本有差异,导致编译的class不一致。

要解决上面环境的问题,需要保持在测试覆盖率过程中编译的机器环境保持一致,或者做到只编译一次,使用同一份class文件,考虑到存储空间的问题,vivo采用保持环境一致的办法来解决。

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:45400272-bca5-45af-9e85-6791881c84e1

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:8fda77c1-2357-4c7e-bb06-84c82513348e

4.2 在研发过程中更加关注增量代码的覆盖率

在我们日常的研发活动中,对于全量代码更多使用自动化脚本来回归,而新研发的功能主要表现为增量代码,对于增量代码的覆盖率情况更加关注, JaCoCo本身不支持增量代码的覆盖率。

对于这个问题网上也有不少解决方案,基本都是基于git的版本差异,在生成报告时过滤掉没有差异的类,形成两份覆盖率报告,一份是全量代码覆盖率报告,一份是增量代码覆盖率报告,而我们更希望在一份覆盖率报告中呈现增量代码和全量代码的覆盖情况,结合代码在全量报告中的覆盖路径分析遗漏的场景,同时能在报告中标注增量代码和增量代码的覆盖情况,期望的效果如下图所示:

vivo 基于 JaCoCo 的测试覆盖率设计与实践

vivo 基于 JaCoCo 的测试覆盖率设计与实践

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:9b50cacf-3961-49c2-a3ae-29246dc2a79c

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:be589e7b-c148-459e-8a53-fce9f4f8b1bf

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:7e38494b-b33b-44bf-b9cc-10fb04ea8b2a

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:561a8e39-e437-4e7c-8ea0-902b556c0518

* 改造JaCoCo计算逻辑,针对增量代码单独统计覆盖率指标值
* 改造JaCoCo报告格式,在报告中兼容全量代码和增量代码的覆盖情况

对于计算代码分支的变动情况,放弃 GitLab 提供的代码比对功能来获取不同版本之前的差异信息,如果版本之间差异太多的话,经常发生GitLab 的API接口调用超时;

并且GitLab 的比对功能无法满足定制场景,比如一行代码仅仅因为格式化被识别为变更代码等等,采用借助Linux自带的diff命令,实现代码差异比对的能力:

vivo 基于 JaCoCo 的测试覆盖率设计与实践

对于改造 JaCoCo计算逻辑,增加针对增量代码的覆盖率指标统计,在CoverageNodeImpl类中增加新的Counter,用于统计新增类、方法、行、指令覆盖率指标;在SourceNodeImple类中increment方法中增加新增代码行的统计逻辑。

vivo 基于 JaCoCo 的测试覆盖率设计与实践

vivo 基于 JaCoCo 的测试覆盖率设计与实践

4.3 重谈关于classid的问题

在上面已经谈到关于classid的问题,如果是环境问题是比较好解决,但是现在互联网团队基本都使用敏捷模式,基本不太可能等开发工作全部完成再转测,这样必然会导致最新的覆盖率报告,会出现以类为单元的覆盖率数据丢失,需要测试人员来回重复的执行测试案例,否则测试覆盖率数据不会很好看。

既然知道问题所在,那有没有办法解决呢?是不是可以直接找到以前的classid,把以前的classid对应的探针数据复制到当前的classid下就可以?当然是不行的,因为源代码发生变动,导致探针的数量发生变化,会出现下面的情况:

vivo 基于 JaCoCo 的测试覆盖率设计与实践

或者这样

vivo 基于 JaCoCo 的测试覆盖率设计与实践

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:04115ec7-77e1-4dfd-8868-e9b295f82f4a

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:91d7d151-bd9c-46d9-99b2-d78f3dc8d372

vivo 基于 JaCoCo 的测试覆盖率设计与实践

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:2b5ea48e-2193-48e1-bb35-dd04b8ad26dc

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:27d0acea-ae41-4a2a-bf09-875dc6689f09

五、总结

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:40cfc2c2-5544-44e8-ab2c-294d250acf94

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:95379401-96c4-486a-9945-e6129b1f32ba

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:96b6e73c-8e04-4216-9d46-559e2eb24bcd

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:206e10d2-114d-4872-baec-88af9a308762

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:3119189f-ca45-4779-b547-172278d93f9c

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:3b05661f-0e61-4068-9f64-13a9d6ae6ab8

Original: https://www.cnblogs.com/vivotech/p/16627491.html
Author: vivo互联网技术
Title: vivo 基于 JaCoCo 的测试覆盖率设计与实践

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/562231/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球