使用mockk mock kotlinx.datetime的TimeZone时报NoClassDefFoundError如何解决
错误产生原因
- kotlinx.datetime 0.2.1版本内置了序列化支持逻辑,相关方法的签名引用了
kotlinx.serialization.KSerializer类,但kotlinx-serialization属于该版本的可选依赖,默认不会被传递引入到你的项目运行时类路径中 - mockk执行
mockkObject(TimeZone)操作时,会通过反射扫描TimeZone伴生对象的所有声明方法,这个过程会触发对KSerializer类的加载,类路径缺失对应类就抛出了NoClassDefFoundError
解决方案
方案1:补充测试依赖
仅在测试依赖中添加kotlinx-serialization即可,不需要改动生产依赖配置,以Gradle配置为例:
// build.gradle.kts testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2")
依赖版本和你项目使用的Kotlin版本匹配即可,Kotlin 1.5.21对应1.2.x版本的kotlinx-serialization即可正常运行。
方案2:升级kotlinx-datetime版本
0.3.0及以上版本的kotlinx-datetime已经将序列化逻辑拆分到独立的kotlinx-datetime-serialization组件中,核心库不再包含序列化相关代码,默认不会触发KSerializer的加载,升级依赖即可解决报错:
// 替换原有kotlinx-datetime依赖声明 implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0")
方案3:封装时区获取逻辑(最佳实践)
直接mock第三方库的伴生对象属于强依赖库内部实现的用法,容易受库版本变动影响,更稳妥的方式是在业务代码中封装时区获取接口,测试时仅mock自己定义的接口即可,完全规避对kotlinx-datetime内部逻辑的依赖:
// 业务代码定义接口 interface TimeZoneProvider { fun getCurrentDefault(): TimeZone } // 生产环境默认实现 object SystemTimeZoneProvider : TimeZoneProvider { override fun getCurrentDefault() = TimeZone.currentSystemDefault() } // 测试代码示例 every { mockTimeZoneProvider.getCurrentDefault() } returns TimeZone.of("Asia/Tokyo")
该方案不需要修改任何第三方依赖配置,兼容性和可维护性最高。
内容的提问来源于stack exchange,提问作者Ippei Mukaida
相关产品推荐
相关产品推荐

