Jacoco 抱怨不太可能丢失分支覆盖

Jacoco complains about unlikely missing branch coverage

我正在使用 Gradle 6.3 和 Jacoco 来编译、测试和显示覆盖率报告。但我不明白为什么它抱怨“错过了 2 个分支中的 1 个”,根本没有分支:

这是完整的 Kotlin 数据class:

data class ListNode<T>(var value: T, var next: ListNode<T>?) {

    override fun hashCode(): Int = value.hashCode()

}

如果幕后有分支,它们是什么,我该如何覆盖它们?

Kotlin 中的 data class 具有默认的自动生成方法(equalstoStringhashCode、getter、setter)。在这种情况下,我们谈论的是 equals。在class ListNode的代码中是看不到的,但它的JVM字节码肯定包含equalstoString

Jacoco 主要针对 Java Code Coverage,因此,这种情况可能会被视为 Jacoco 对 Kotlin 中生成的方法的错误处理。如果您愿意 - 为项目维护者提交 issue

无论如何,为了避免 Jacoco 对这种特殊情况发出警告,只需查看报告中的代码(第 17,18 行,.. 直到 equals 方法的末尾):

  1. 暂时将生成的equals方法的内容复制到ListNode(只是为了自己方便)

  2. 将代码覆盖率增加到需要的水平

  3. 擦除equals方法

  4. 代码覆盖率与步骤 (2) 相同,因为 equals 保持不变(但现在它是隐式创建的)

字节码中实际上有两个分支,原因如下。

您正在 Any? 上使用扩展名 hashCode(),因为 T 的上限是 Any?,而不是 Any。这个扩展是这样实现的:

public inline fun Any?.hashCode(): Int = this?.hashCode() ?: 0

这意味着在字节码中,您对 value.hashCode() 的调用被替换为 value?.hashCode() ?: 0

在您的测试中,您只涵盖了 value 可能不为空的情况,因此未涵盖分支 ?: 0

在 JaCoCo 不支持 inline 函数之前,您有两个解决方案:

  • 还包括使用 null T.
  • 测试 ListNode 的其他分支
  • 使用 Any 作为 T 的上限:
data class ListNode<T: Any>(var value: T, var next: ListNode<T>?)