Kotlin中Elvis运算符安全调用引发Jacoco分支覆盖率缺失问题
Kotlin 1.7.10中Jacoco显示空安全表达式未完全覆盖的原因
在Kotlin 1.7.10版本里,一段包含空安全调用和Elvis表达式的代码被Jacoco报告为4个条件仅覆盖3个,但明明测试已经覆盖了street不为空和为空两种场景,换Kotlin 1.6.10则显示全覆盖。下面是具体代码和问题解析:
业务代码
data class House(val line1: String) data class Street(val house: House) fun mapCheck(key : String) : House { val map = mapOf("A" to Street(House("10 London Road"))) val street = map[key] return street?.house ?: throw IllegalArgumentException("unknown key") }
测试代码(Kotest + JUnit 5)
@Test fun mapTest() { val factory = dpsServiceFactory() factory.mapCheck("A").line1 shouldBe "10 London Road" shouldThrow<java.lang.IllegalArgumentException> { factory.mapCheck("V") } }
未覆盖的条件是什么?
问题出在Kotlin 1.7.10的字节码生成逻辑上——Jacoco是基于字节码统计覆盖率,而非Kotlin源码。
源码里的street?.house ?: throw ...,在1.7.10的字节码中被拆成了4个逻辑分支:
- 分支1:
street不为null → 执行street.house - 分支2:
street为null → 进入Elvis分支抛异常 - 分支3:
street.house不为null → 返回该值 - 分支4:
street.house为null → 进入Elvis分支抛异常
但你的Street类里house是非空类型(val house: House),编译期就能确定street.house不可能为null,这个分支在业务中永远不会触发。但Jacoco依然会统计这个字节码层面的分支,而你的测试没覆盖它,所以显示4个条件只覆盖了3个。
而Kotlin 1.6.10的字节码没生成这个额外的非空检查分支,所以Jacoco显示全覆盖。
处理方式
如果想让覆盖率拉满,可以临时把Street的house改成可空类型(val house: House?),加一个测试用例触发street.house为null的场景。但实际业务中这个分支不存在,所以更合理的方式是把它当成Jacoco的假阳性结果忽略即可。
内容的提问来源于stack exchange,提问作者PaulNUK
相关产品推荐
相关产品推荐

