如何移除单元测试中冗余的双感叹号(!!)非空断言?
Kotlin 单元测试非空断言智能转换问题解决方案
问题原因
JVM 平台普通 assert 语句默认仅在开启 -ea 断言参数的运行环境下生效,正式编译打包时会被直接剥离,因此 Kotlin 编译器不会将 assert(password != null) 的判断结果作为非空依据,无法触发非空智能转换。
优化方案
方案1:使用 Kotlin 标准库非空校验函数
checkNotNull、requireNotNull 等标准库函数自带 Kotlin 契约声明,校验通过后编译器会自动将变量识别为非空,无需额外添加非空断言:
@Test public fun `password is not null and has the exact length between 1 and 64`() { repeat(64) { mockPasswordLengthPreferenceBy(it + 1) val password = passwordGenerator.generatePassword() val nonNullPassword = checkNotNull(password) assertTrue(nonNullPassword.length == it + 1) assertTrue(passwordGenerator.validatePassword(nonNullPassword)) } }
注:原代码存在长度校验逻辑问题,你设置的密码长度为
it+1,正确校验逻辑应为判断长度等于it+1而非it。
方案2:使用带契约支持的测试断言
新版 kotlin-test 库提供的 assertNotNull 断言已添加契约支持,校验通过后可直接触发非空智能转换:
@Test public fun `password is not null and has the exact length between 1 and 64`() { repeat(64) { mockPasswordLengthPreferenceBy(it + 1) val password = passwordGenerator.generatePassword() assertNotNull(password) assertTrue(password.length == it + 1) assertTrue(passwordGenerator.validatePassword(password)) } }
如果使用的是不支持契约的旧版本断言,可直接在变量声明时添加兜底逻辑触发智能转换:
val password = passwordGenerator.generatePassword() ?: error("生成密码不可为空")
方案3:使用安全调用+作用域函数
仅需要在非空场景下执行后续逻辑时,可直接使用安全调用搭配 let 函数,无需额外校验:
@Test public fun `password is not null and has the exact length between 1 and 64`() { repeat(64) { mockPasswordLengthPreferenceBy(it + 1) passwordGenerator.generatePassword()?.let { password -> assertTrue(password.length == it + 1) assertTrue(passwordGenerator.validatePassword(password)) } ?: fail("生成密码不可为空") } }
内容的提问来源于stack exchange,提问作者René Jörg Spies
相关产品推荐
相关产品推荐

