研发效能 Jacoco 覆盖率合并以及精准测试中的调用链路分析

alwans · 2023年06月20日 · 最后由 anankoushuiwa 回复于 2024年10月29日 · 9171 次阅读

目标

代码覆盖率

  • 可合并不同版本的覆盖率数据 (合并的颗粒度是代码逻辑分支)
  • 可实时查看最新的覆盖率报告

链路分析

  • 记录函数调用链:最小单元为函数中的代码逻辑分支
  • 自动关联接口用例和功能用例。通过分析代码变更记录,自动推荐受影响的接口用例和功能用例

代码覆盖率

为了后面查看覆盖率报告比较直观,测试代码都放在同一个类中
测试代码如下:

@RestController
@RequestMapping("/api/account")
public class LoginController {

    @GetMapping("/login")
    public Result login(@RequestParam(required = false) String name,
                        @RequestParam(required = false) String passwd){
        if (StringUtils.isEmpty(name)){
            System.out.println("用户名不能为空");
            return Result.error("用户名不能为空");
        }
        if (StringUtils.isEmpty(passwd)){
            System.out.println("用户密码不能为空");
            return Result.error("用户密码不能为空");
        }
        if (!"123456".equals(passwd)){
            System.out.println("用户密码错误");
            return Result.error("用户密码错误");
        }
        test1(name);
        if ("admin".equals(name)){
            System.out.println("管理员登录成功");
            return Result.success("管理员登录成功");
        }
        System.out.println(name+"登录成功");
        return Result.success(name+"登录成功");
    }

    @GetMapping("/info")
    public Result info(){
        return Result.success("用户信息获取成功");
    }

    private void test1(String name){
        if ("admin".equals(name)){
            System.out.println("登录用户是管理员, 拥有全部权限");
        }
        else {
            System.out.println("当前是普通用户,设置部分权限");
            new Runnable(){
                @Override
                public void run() {
                    System.out.println("设置登录用户积分");
                }
            }.run();
        }
        System.out.println("权限处理完毕");
    }
}

在这段代码中,login 接口存在 5 条可执行调用链的情况,分别对应 http 请求信息如下:

  • 127.0.0.1:7020/api/account/login
  • 127.0.0.1:7020/api/account/login?name=test
  • 127.0.0.1:7020/api/account/login?name=test&passwd=111111
  • 127.0.0.1:7020/api/account/login?name=test&passwd=123456
  • 127.0.0.1:7020/api/account/login?name=admin&passwd=123456

依次执行上述 5 条请求 ,点击生成覆盖率报告如下:

可以看到 login 接口的 5 条调用路径已全部执行

修改测试代码:

提交代码,重新部署测试服务
请求 info 接口:

  • 127.0.0.1:7020/api/account/info

平台页面上点击生成覆盖率报告:

从报告中可以看出保留了第一次收集数据中有效的部分数据,同时合并了重新部署服务后请求 info 接口的的代码覆盖率数据。
去除了第一次收集数据中的无效数据:test 用户登录相关代码变更,所以 test 用户登录的无效覆盖率数据被清除。

调用链分析

关联接口用例

  1. 在接口自动化模块的测试计划配置中,可一键开启覆盖率收集、流量录制以及录制调用链功能

  2. 在生成的测试报告中,可查看覆盖率数据以及每条用例对应的函数调用链

  3. 同时支持直接自动 mock 回放该接口请求,实现快速定位接口问题

分析调用链

调用链分析采用动静结合。通过关联用例获取函数动态调用链,调用链数据用 es 进行保存。分析代码变更,可准确获取到受影响的用例数据。对于部分新增函数通过静态代码分析,静态调用链数据用 neo4j 进行保存。静态代码分析在设计中主要用来弥补对于新增的入口函数,没有录制调用链,然后通过分析源码的函数调用链,来确认受影响的范围,这种方式就无法准确获取到受影响的接口用例。所以主要还是依赖动态调用链来做。

平台上请求分析调用链影响变更

对于上图结果,大致分为精确分析出受影响的接口用例等数据以及通过静态代码分析获取到的影响接口用例的范围数据。

历史帖

共收到 17 条回复 时间 点赞

给大佬点赞

历史贴的 id 错了哦

有开源计划么?

暂时没有开源计划

给大佬点赞

大哥,你上篇帖子公布的覆盖率平台能直接使用吗?我这生成差异报告找不到 master 分支,生成全量报告命令参数无 source 和 class
: jacoco report 参数:[report, /home/scfadmin
/code-diff/技术中台/T1/demo-coverage/235a140b-d303-4694-9cc1-b72ac47d3702/dump.exec, --html, /home/scfadmin/code-diff/技术中台/T1/demo-coverage/235a140b-d303-4694-9cc1-b72ac47d3702, --filterRules, ["",""]]

仅楼主可见

大神你好,有两个问题想咨询一下:1)jacoco 增加调用链记录的代码后,会影响原有探针逻辑吗?2)执行每条用例(不管手动还是自动化)后,如何将用例和所录制调用链树形数据做隔离:用例 1->调用链 1 用例 2->调用链 2

wangleilei 回复

1.不会影响。2.调用链增加了用例标识

提交代码,重新部署测试服务
请求 info 接口:
127.0.0.1:7020/api/account/info

请教下 为什么只请求了 info 接口,合并的覆盖率报告 login 方法中 if 语句和登录成功会标记为未覆盖?

仅楼主可见

想问下 jacoco 是如何把用例和后端接口关联起来的,是做了 tag 标识来进行识别的吗

4楼 已删除
仅楼主可见
alwans #16 · 2024年08月20日 Author
小小 回复

你的微信号

仅楼主可见

楼主,能否加个 V 沟通下 调用链问题,及和用例关联的问题?weChatNo = seguzhizi

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册