Jacoco | 查询某测试类针对特定方法的覆盖率

发布时间:2024年01月15日

背景

新的科研idea是覆盖率引导的测试生成,里面需要用到查询某个测试类对被测方法的覆盖情况,想到了最常用的jacoco,在chatgpt的帮助下实现了这个方法。

启动测试

jacoco插件配置

需要在项目的pom.xml中加入以下内容

<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>0.8.11</version>
  <executions>
    <execution>
      <goals>
        <goal>prepare-agent</goal>
      </goals>
    </execution>
    <execution>
      <id>report</id>
      <phase>test</phase>
      <goals>
        <goal>report</goal>
      </goals>
    </execution>
  </executions>
</plugin>

命令行执行测试

再在teminal中执行(如果有多个测试类,可以用逗号隔开)

mvn test -Dtest="org.apache.commons.csv.generated_by_chatgpt.CSVFormatTest"

运行成功截图

解析jacoco.xml

这时,能够在target/site下看到jacoco.xml

我们可以在在jacoco.xml中首先定位类标签

<class name="org/apache/commons/csv/CSVFormat" sourcefilename="CSVFormat.java">

然后定位方法标签

<method name="hashCode" desc="()I" line="554">

就能看到CSVFormatTest针对CSVFormat的hashCode这一方法的覆盖率情况了

五个标签的含义如下

  • INSTRUCTION: 表示在字节码级别的指令覆盖率,有92个指令被覆盖,16个未被覆盖。
  • BRANCH: 表示分支覆盖率,有7个分支被覆盖,7个未被覆盖。这通常指的是所有的if、switch语句中的不同代码路径。
  • LINE: 表示行覆盖率,共有12行被覆盖,0行未被覆盖。行覆盖率指的是代码中的单行是否被执行。
  • COMPLEXITY: 表示圈复杂度覆盖率,有1个圈复杂度被覆盖,7个未被覆盖。圈复杂度是衡量程序复杂性的一个指标,它基于代码中线性代码段(也称作基本块)的数量和控制结构(如条件语句、循环)之间的关系。
  • METHOD: 表示方法覆盖率,显示hashCode这个方法被完全覆盖,即在测试中被调用了。

对我来说我需要获取字节码指令和分支这两个属性遗漏覆盖的情况,为此我写了一个Python方法,用xml.etree.ElementTree来解析jacoco.xml,代码如下

import xml.etree.ElementTree as ET

def check_missed_value():
    """
    查询特定项目某个方法运行后生成的jacoco.xml
    返回遗漏的被测方法的字节码指令数量和分支数量
    """
    jacoco_xml_path = r"C:\dataset\d4j-spec5\3_csv\4f\4f\target\site\jacoco\jacoco.xml"

    # 加载XML文件
    tree = ET.parse(jacoco_xml_path)
    root = tree.getroot()

    # 寻找特定的<class>标签
    class_name = "org/apache/commons/csv/CSVFormat"
    source_file_name = "CSVFormat.java"
    method_name = "hashCode"
    method_desc = "()I"

    for cls in root.iter('class'):
        if cls.get('name') == class_name and cls.get('sourcefilename') == source_file_name:
            # 在找到的<class>标签中寻找特定的<method>标签
            for method in cls.iter('method'):
                if method.get('name') == method_name and method.get('desc') == method_desc:
                    # 显示所有的<counter>标签
                    for counter in method.iter('counter'):
                        if counter.get('type') == "INSTRUCTION":
                            missed_instruction = int(counter.get('missed'))
                        elif counter.get('type') == "BRANCH":
                            missed_branch = int(counter.get('missed'))
                    if missed_instruction is not None and missed_branch is not None:
                        return (missed_instruction, missed_branch)
                    
    return None

文章来源:https://blog.csdn.net/weixin_44997802/article/details/135608814
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。