• Java 系语言测试覆盖率的半个解决方案


    一个多月前在社区开源了自己的一个 JVM 测试工具,然后看大家可能比较关注与其相关的测试覆盖率功能,所以我决定花点时间把这块功能单独拿出来详细分享给大家。

    先把之前的帖子和工具链接贴一下,可以先看链接,也可以直奔下文

    开源工具:

    remote-debug-agent,wiki 中也有对测试覆盖率的详细说明。

    https://github.com/uniquetruth/remote-debug-agent

    首先要做一些 限定 说明

    1. 仅限 Java 或其它 JVM 系语言。因为测试覆盖率跟具体的编程语言密切相关,我也只对 JVM 系的语言有研究,如果您想看别的语言的解决方案,还得另寻高明。

    2. 只讨论后端程序的覆盖率。

    3. 适用集成测试。关于单元测试的覆盖率请直奔Jacoco。

    4. 适用增量开发,增量测试的系统。

    为什么不用 Jacoco

    其实是对上面 3、4 两点做一些说明。不是说 Jacoco 不能用于集成测试,只是每个工具有每个工具的特点和局限性。从我自身的实际经验来看,集成测试有一个问题是我比较关心的,那就是  一般集成环境只有 1 个,多个需求会同时放上去测,可能每个测试人员也就负责 1、2 个需求,我如何知道具体某一个需求的覆盖率?  

    仅靠原生的 Jacoco 似乎难以解决这个问题,因为它默认只为整个系统的代码生成一个覆盖率报告。

    单元测试和 Jacoco 的思路是一个质量保障的理想方向。一个系统一次 build 确实应该把所有的功能测试一遍。但理想很丰满现实却很骨感,也许真的有厉害的团队能做到这点,但就我自身的实际情况来看,我们离那种境界还差得很远很远。

    我们大部分时间是对一个已上线系统进行优化/补丁开发,然后系统整体很庞大,功能很多,别说集成阶段的手工测试了,就算是用上自动化测试,也不可能把所有功能都跑一遍。为某个需求写的单测案例,也顶多在该需求开发的时间点附近可以跑起来,远达不到长期反复测试使用的需要。

    近年来的系统微服务化确实可以在一定程度上缓解单一系统臃肿问题,但服务数量的增加,也会让服务关联变得复杂,有时在单测环节想把以前的案例都跑一遍,却发现写 mock 都要写断手。

    此外集成环境还要面对稳定性问题,比如频繁的发布重启。我的需求发布后,我才测了 30%,系统就因发布重启了,一重启 Jacoco 就生成了一份覆盖率数据,那么我测试整个需求所覆盖的代码就会存在于多个 exec 文件中,如何整合整个需求测试时覆盖了哪些代码也是一个难题。

    OK,说了那么多,这里先坦言一下,我的工具也不能解决上述所有问题,但 1.至少解决了部分问题;2.给剩下的问题也提供了解决它们的 “接口”。这也是标题所写 半个解决方案 的原因。

    最终的解决方案是完全可行的,因为我目前正在自己的实际工作中,依靠本方案为团队提供单一需求级的测试覆盖率报告。

    正文

    关于我的工具本身的介绍请看前面的帖子,或者看 Github 的 wiki,帖子里是简介,wiki 上我写得很详细。这里我只介绍与测试覆盖率相关的内容。

    部署

    下载或编译工具后可以得到一组 jar 包,remote-debug-agent.jar 就是工具本体,其它是关联 jar 包。首先将所有 jar 包放到服务器的任意目录中,然后工具是作为 javaagent 运行的,所以要修改被测应用的启动命令(如果不想修改启动脚本也可以使用热部署的方式,详见 wiki),需要将-javaagent 参数添加到 JAVA_OPTS 中。

    举个例子,假如你的被测系统中的代码(你想要关注覆盖率的代码)都在 com.foo.bar 这个包底下(或其子包底下),那么就添加

    -javaagent:${你的目录}/remote-debug-agent.jar=includes=com.foo.bar,apiport=8098

    到 JAVA_OPTS 中,之后就可以正常启动应用了。

    应用启动后 agent 就默认处于工作状态了,本工具有 2 种工作模式:线程隔离模式和非线程隔离模式。

    线程隔离模式

    这是工具默认的工作模式,它会将 JVM 内的线程与请求调用者的身份进行绑定,之后再记录覆盖率。举个例子,A 测试员测试需求甲,B 测试员测试需求乙,他们同时在一个集成环境上做手工测试。A 点了一个按钮,运行了后端的代码 com.foo.bar.Class1.method1(),B 点了另外一个按钮,运行了代码 com.foo.bar.Class2.method2(),那么 agent 的记录就是 A 覆盖了 method1 的某几行,B 覆盖了 method2 的某几行。一般情况下也就可以认为测试需求甲和需求乙时分别覆盖了哪些代码。

    这里要说明两个问题

    1. 对于工具来说,A 和 B 的身份是什么?本工具的设计理念是尽量减少对测试人员的影响,因此测试人员只需要正常的执行测试即可。他们发送到服务器后端的请求是未经过任何装饰的,所以工具默认使用客户端 IP 作为身份标识,在我的工作环境中,大部分系统的集成环境都部署在公司内网环境中,而测试人员的办公电脑 IP 都是固定的,这样 IP 地址就是人的身份标识。当然也有一些系统可能部署在互联网环境,或者有的人的办公 IP 不是固定的,我提供的解决思路是从请求中提取其它可标识身份的信息,比如 HTTP header 中的

  • 相关阅读:
    ASP .NET Core API(swaggerUI)实例demo下载、发布与部署(各种遇到的坑、解决方法)
    winform绘制圆形控件抗锯齿
    博士论文答辩流程
    Activity 的销毁流程
    浅谈中资互联网券商出海零售经纪业务的策略与路径
    Linux常用指令
    Rockland丨Rockland 抗体修饰-抗体偶联方案
    被CTO推荐的SQL总结
    LeetCode知识点总结 - 508
    Flink中序列化RoaringBitmap不同方式的对比
  • 原文地址:https://blog.csdn.net/JHIII/article/details/126542351