为什么Jacoco分行覆盖率报告会说a && b && c是否实际上是6个分行?

如何解决为什么Jacoco分行覆盖率报告会说a && b && c是否实际上是6个分行?

我的公司要求新代码必须达到90%的测试覆盖率,而对于Java代码,我正在使用gradle jacoco插件,这很好。但是,当分支的数量开始呈指数增长(夸张的,可能是几何增长)时,很难将分支覆盖率提高到90%。

这是一个非常人为的例子:

public class Application {
    public static void test(boolean a,boolean b) {
        if (a && b) {
            System.out.println("true!");
        } else {
            System.out.println("false!");
        }
    }
}

测试:

public class ApplicationTests {
    @Test
    public void test() {
        Application.test(true,true);
        Application.test(false,false);
    }
}

以下是覆盖率报告的样子:

Test Report Screenshot

它还说我错过了4个分支中的1个,换句话说,我已经覆盖了4个分支中的3个(75%的分支覆盖率)。

如果我在这里增加布尔值的数量,则分支的数量似乎是n * 2,其中n是布尔值的数量。因此3(a,b,c)变成6个分支,而10个变成20个分支。所以我想我不明白在这种情况下有6或20个分支的含义。

要满足这个问题-我可以

A)将jacoco配置为更直观,并将if / else条件视为始终具有2个分支(分支1是if执行时​​的分支,分支2是else执行时的分支)-子表达式的惰性执行可以是跟踪为线路覆盖率或其他内容。

B)为了更完整地解释为什么它说这些if / else有4、6、20个分支,并将2、3、10个布尔值组合成1个表达式。

编辑-澄清混乱的来源:

  1. 在只有2个电话的情况下,我如何覆盖3个分支?
  2. 为什么示例中3个布尔值(a && b && c)的分支数量达到6个分支,而10个布尔值(a && b && c && .. && j)却达到20个分支?

如果每个布尔值为true或false,然后我同时调用两种状态的函数,那么在这里我怎么没有得到100%的分支覆盖率?我想念一些东西。

解决方法

在编写类似if(a && b)的条件时,在运行测试用例时,它将适用于下面提到的所有四个方案。

{
    applicationVersion: '4194309',application: 'com.domain.app',scope: '*',authorizedEntity: '913674269572',rel: { topics: { topicName: { addDate: '2020-08-19' } } },appSigner: 'ae7cbSomeHash23jsdfff34ac7ffd',platform: 'ANDROID'
} 

因此,您需要调用此方法四次以涵盖 result a b true true true false false true false true false false false false

您还可以创建一些实用程序类,该实用程序类将根据参数数量生成这些方案。

,

现有答案部分解释了给定的示例,但我想在这里添加一个更一般的观点:Jacoco 分析字节码,分支覆盖率仅计算其中的二进制条件语句(分支)的目标。

给定上面的例子,但有三个变量,我们得到 6 个分支。

snippet

查看字节码我们看到短路操作符被翻译成三个ifeq,代表分支语句。他们每个人都有两个可能的目标,一共六个。

  public static void test(boolean,boolean,boolean);
    Code:
       0: iload_0
       1: ifeq          23
       4: iload_1
       5: ifeq          23
       8: iload_2
       9: ifeq          23
      12: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      15: ldc           #22                 // String true!
      17: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      20: goto          31
      23: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      26: ldc           #30                 // String false!
      28: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      31: return

如何获得全覆盖可以在对应的控制流图中看到:第一个测试用例test(true,true,true)沿着顶部的路径,覆盖了三个分支。对于剩下的分支,我们还需要三个测试用例,每个都在短路算子处进行另一个“出口”。

cfg

100% 分支覆盖率所需的测试用例不会随着条件中子表达式的数量呈指数增长 - 事实上它是线性的。然而,要求覆盖子条件的所有可能组合(这里:a、b 和 c 的值)被称为多条件覆盖(wikipedia:coverage)。 Jacoco 无法检查,因为字节码只知道二进制条件语句。

,

所以我想我现在想出了为什么分支数量等于n * 2的原因,其中n是if()条件内的布尔表达式的数量。

每个布尔表达式都是其自己的分支,因此在此示例中,如果我们有a && b && c,则有3个不同的表达式,每个表达式具有2个状态,因此有6个分支。要覆盖所有6个分支,测试必须确保在正确和错误状态下评估每个变量。关键部分是必须对每个表达式进行求值,在某些情况下,这并不是因为Java中的惰性求值。

public class Application {
    public static void test(boolean a,boolean b,boolean c) {
        if (a && b && c) {
            System.out.println("true!");
        } else {
            System.out.println("false!");
        }
    }
}

对于示例if (a && b && c),当传递所有a的{​​{1}},bc值时,实际上一次执行覆盖了3个分支。但是,如果全部以true的形式传递,则它仅覆盖一个分支,因为false的错误和惰性评估不会检查bc

在这种情况下,要有效覆盖所有6个分支,必须多次调用test函数以达到100%的分支覆盖率。

a
,

在此示例中,您实际上只需要进行3次测试即可获得100%的覆盖率。在两者均为假的情况下进行测试不会提供任何额外的保障。凭直觉,这应该是有道理的。除非它的至少一个参数为false,否则您希望它显示为true。

构造代码的方式也会影响分支的数量。如果要求做一件事,那么当所有事情都为真时执行,而当其中任何一个都不为假时,则可以执行以下操作:

if (Stream.of(a,b).reduce(Boolean::logicalAnd).get(){
   System.out.println("true");
} else {
   System.out.println("false");
}

在一个只有两个输入的人为示例中,它看起来有点愚蠢。如果在实际环境中有两个以上的输入,则可能更有意义。例如,您可能有一个List<ValidationRule>之类的东西,并且每个元素都计算一个布尔值。我不会再说太多了,因为它超出了您最初提出的问题的范围,但这可能值得考虑。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-