为何单元测试用例会先于伴生对象中的init块执行?
问题原因及解决方案
核心原因
Kotlin(以及Java)中,枚举类的初始化(包括其伴生对象的init块)是懒加载的:只有当第一次访问该枚举类的任何成员(比如枚举实例、调用values()方法、访问伴生对象成员)时,JVM才会触发枚举类的类加载与初始化流程,而非程序启动时就自动初始化所有枚举类。
你遇到的执行顺序问题本质是:
enumB在测试用例执行前就被其他代码(比如ObjectMapper的序列化/反序列化逻辑、其他依赖代码)访问过,提前触发了初始化;enumA是在测试断言的InterfaceA.EnumA.SomeInstance处第一次被访问,此时才触发其伴生对象的init块;enumC在测试用例执行完毕前都未被任何代码访问,初始化被推迟到测试之后。
解决方案
要确保所有枚举类的init块在测试前执行,只需在测试开始前显式触发每个枚举类的初始化,具体可通过以下方式实现:
1. 在测试前置方法中手动触发
使用测试框架的前置注解(如JUnit 5的@BeforeAll),在方法中显式访问每个枚举类的成员,比如调用values()方法:
@BeforeAll fun initAllEnums() { // 触发所有枚举类的初始化,执行伴生对象init块完成注册 EnumA.values() EnumB.values() EnumC.values() } @Test fun aTest(){ val objectMapper = ObjectMapper() val readValue: InterfaceA = objectMapper.readValue(jsonString, InterfaceA::class.java) MatcherAssert.assertThat(readValue.XXXType, Is.`is`(InterfaceA.EnumA.SomeInstance)) }
调用values()方法会强制JVM加载并初始化对应的枚举类,进而执行伴生对象的init块完成注册。
2. 统一注册入口(可选优化)
如果枚举类数量较多,可创建统一的注册类,集中管理所有需要初始化的枚举类,再在测试前置方法中调用初始化:
object EnumRegistrar { fun registerAll() { EnumA.values() EnumB.values() EnumC.values() } } @BeforeAll fun initEnums() { EnumRegistrar.registerAll() }
内容的提问来源于stack exchange,提问作者wl.GIG
相关产品推荐
相关产品推荐

