kotlin - Kotlin 协程的 Jacoco 代码覆盖率不正确

标签 kotlin code-coverage spock kotlin-coroutines jacoco

我正在使用Jacoco进行单元测试代码覆盖率。 Jacoco 生成的报告显示,我的 Kotlin 代码遗漏了很少的分支。我注意到根据 Jacoco 的说法,协程代码及其后面的代码没有被正确覆盖。我不确定是因为协程还是其他什么原因。 使用 IntelliJ 代码覆盖率运行单元测试时,我的 Kotlin 类显示100% 覆盖率

我不知道为什么 Jacoco 的报道较少。我已经使用 Spock (Groovy) 编写了单元测试。

请引用以下图片:

错过的分支: enter image description here

enter image description here

原始代码: enter image description here

最佳答案

类似于“Why is JaCoCo not covering my String switch statements?”:

JaCoCo 执行字节码分析,而不是源代码。使用 kotlinc 1.3.10

编译 Example.kt
package example

fun main(args: Array<String>) {
    kotlinx.coroutines.runBlocking { // line 4
    }
}

生成两个文件 ExampleKt.classExampleKt$main$1.class,最后一个文件的字节码 (javap -v -p ExampleKt$main$1.class) class) 包含方法 invokeSuspend(Object)

  public final java.lang.Object invokeSuspend(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
    flags: ACC_PUBLIC, ACC_FINAL
    Code:
      stack=3, locals=4, args_size=2
         0: invokestatic  #29                 // Method kotlin/coroutines/intrinsics/IntrinsicsKt.getCOROUTINE_SUSPENDED:()Ljava/lang/Object;
         3: astore_3
         4: aload_0
         5: getfield      #33                 // Field label:I
         8: tableswitch   { // 0 to 0
                       0: 28
                 default: 53
            }
        28: aload_1
        29: dup
        30: instanceof    #35                 // class kotlin/Result$Failure
        33: ifeq          43
        36: checkcast     #35                 // class kotlin/Result$Failure
        39: getfield      #39                 // Field kotlin/Result$Failure.exception:Ljava/lang/Throwable;
        42: athrow
        43: pop
        44: aload_0
        45: getfield      #41                 // Field p$:Lkotlinx/coroutines/CoroutineScope;
        48: astore_2
        49: getstatic     #47                 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;
        52: areturn
        53: new           #49                 // class java/lang/IllegalStateException
        56: dup
        57: ldc           #51                 // String call to 'resume' before 'invoke' with coroutine
        59: invokespecial #55                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
        62: athrow
      LineNumberTable:
        line 4: 3
        line 5: 49

它与源文件的第 4 行关联并包含分支(ifeqtableswitch)。

虽然目前最新的 JaCoCo 版本 (0.8.2) 具有针对各种编译器生成的工件的过滤器,例如 switch 语句中的 String、Kotlin 编译器生成的字节码协程没有被过滤。变更日志可以在https://www.jacoco.org/jacoco/trunk/doc/changes.html查看以及其他:https://www.jacoco.org/research/index.html还有presentation about bytecode pattern matching显示/解释了许多编译器生成的工件。

<小时/>

您在 IntelliJ IDEA 中看到的 100% - 只是行覆盖率,因此您正在尝试比较两个完全不同的事物。作为证明 - 这是 IntelliJ IDEA 的屏幕截图,它显示 100% 的行覆盖率,但仅执行了 if 的一个分支(其中 args.size >= 0 计算结果为 true)

intellij

这是执行同一源文件时 JaCoCo 报告的相应屏幕截图

jacoco source level

进入包级别,您可以看到 100% 的线路覆盖率,但 50% 的分支覆盖率

jacoco package level

然后通过第一个链接进入类级别 ExampleKt.main.new Function2() {...} 您可以再次看到该方法 invokeSuspend(Object) 贡献错过的分支

jacoco class level

<小时/>

更新(2019 年 1 月 29 日)

JaCoCo version 0.8.3具有 Kotlin 编译器添加的用于挂起 lambda 和函数的分支过滤器:

before

after

关于kotlin - Kotlin 协程的 Jacoco 代码覆盖率不正确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53485360/

相关文章:

c# - 代码覆盖率将第三方代码纳入覆盖范围

c - 是否有适用于 Windows 的免费 C 测试代码覆盖工具?

kotlin - Android Studio:Gradle同步失败:无法下载修补程序

java - 错误 : Entities and Pojos must have a usable public constructor.

java - 无法识别的属性名称 MODULE(com.sun.tools.javac.util.SharedNameTable$NameImpl 类)

ruby-on-rails - 从 Simplecov 报告中排除供应商文件

grails - 数据驱动的Spock测试

testing - 如何在 Spock 测试中使用 EqualsVerifier

java - Maven/Surefire 无法在同一项目中执行 Spock 和 JUnit

android - 从 PagerAdapter 调用时 fragment 消失