jacoco增量覆盖率实践

本文详细介绍了如何通过jacoco实现增量代码覆盖率,包括差异代码的获取、jacoco插桩逻辑的修改以及report阶段的判断,提供了完整的代码示例和解决参数匹配问题的方法。

Jacoco增量覆盖率说明


能找到这里,说明对jacoco的原理和使用有了一定的了解,而我写这边文章主要是网络上基本没有完整文档加代码的jaocco增量覆盖说明,所以我想分享些东西让需要这方面的人快速去实现自己想要的功能,那么如果想实现增量代码覆盖率需要做到哪些工作呢?

大家在网络上找到的实现方式无外乎三种

  1. 获取到增量代码,在jacoco进行插桩时判断是否是增量代码后再进行插桩,这样需要两个步骤,一是获取增量代码,二是找到jacoco的插桩逻辑进行修改
  2. 获取增量代码,在report阶段去判断方法是否是增量,再去生成报告
  3. 获取差异代码,解析生成的report报告,再过滤出差异代码的报告

首先第一种需要对java字节码操作比较熟悉,难度较高,我们不谈,第三种去解析生成的报告,可能存在误差

所以我们一般选择第二种,而网络上所有的增量实现基本是基于第二种,我们先看看下面的图

上图说明了jacoco测试覆盖率的生成流程,而我们要做的是在report的时候加入我们的逻辑

根据我们的方案,我们需要三个动作

  • 计算出两个版本的差异代码(基于git)
  • 将差异代码在jacoco的report阶段传给jacoco
  • 修改jacoco源码,生成报告时判断代码是否是增量代码,只有增量代码才去生成报告

下面我们逐步讲解上述步骤

计算差异代码

计算差异代码我实现了一个简单的工程:差异代码获取

主要用到了两个工具类

<dependency>
    <groupId>org.eclipse.jgit</groupId>
    <artifactId>org.eclipse.jgit</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/com.github.javaparser/javaparser-core -->
<dependency>
    <groupId>com.github.javaparser</groupId>
    <artifactId>javaparser-core</artifactId>
</dependency>

org.eclipse.jgit主要用于从git获取代码,并获取到存在变更的文件

javaparser-core是一个java解析类,能将class类文件解析成树状,方便我们去获取差异类

/**
 * 获取差异类
 *
 * @param diffMethodParams
 * @return
 */
public List<ClassInfoResult> diffMethods(DiffMethodParams diffMethodParams) {
    try {
        //原有代码git对象
        Git baseGit = cloneRepository(diffMethodParams.getGitUrl(), localBaseRepoDir + diffMethodParams.getBaseVersion(), diffMethodParams.getBaseVersion());
        //现有代码git对象
        Git nowGit = cloneRepository(diffMethodParams.getGitUrl(), localBaseRepoDir + diffMethodParams.getNowVersion(), diffMethodParams.getNowVersion());
        AbstractTreeIterator baseTree = prepareTreeParser(baseGit.getRepository(), diffMethodParams.getBaseVersion());
        AbstractTreeIterator nowTree = prepareTreeParser(nowGit.getRepository(), diffMethodParams.getNowVersion());
        //获取两个版本之间的差异代码
        List<DiffEntry> diff = nowGit.diff().setOldTree(baseTree).setNewTree(nowTree).setShowNameAndStatusOnly(true).call();
        //过滤出有效的差异代码
        Collection<DiffEntry> validDiffList = diff.stream()
                //只计算java文件
                .filter(e -> e.getNewPath().endsWith(".java"))
                //排除测试文件
                .filter(e -> e.getNewPath().contains("src/main/java"))
                //只计算新增和变更文件
                .filter(e -> DiffEntry.ChangeType.ADD.equals(e.getChangeType()) || DiffEntry.ChangeType.MODIFY.equals(e.getChangeType()))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(validDiffList)) {
            return null;
        }
        /**
         * 多线程获取旧代码和新代码的差异类及差异方法
         */
        List<CompletableFuture<ClassInfoResult>> priceFuture = validDiffList.stream().map(item -> getClassMethods(getClassFile(baseGit, item.getNewPath()), getClassFile(nowGit, item.getNewPath()), item)).collect(Collectors.toList());
        return priceFuture.stream().map(CompletableFuture::join).filter(Objects::nonNull).collect(Collectors.toList());
    } catch (GitAPIException e) {
        e.printStackTrace();
    }
    return null;
}

以上代码为获取差异类的核心代码


/**
 * 获取类的增量方法
 *
 * @param oldClassFile 旧类的本地地址
 * @param mewClassFile 新类的本地地址
 * @param diffEntry    差异类
 * @return
 */
private CompletableFuture<ClassInfoResult> getClassMethods(String oldClassFile, String mewClassFile, DiffEntry diffEntry) {
    //多线程获取差异方法,此处只要考虑增量代码太多的情况下,每个类都需要遍历所有方法,采用多线程方式加快速度
    return CompletableFuture.supplyAsync(() -> {
        String className = diffEntry.getNewPath().split("\\.")[0].split("src/main/java/")[1];
        //新增类直接标记,不用计算方法
        if (DiffEntry.ChangeType.A
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值