@Value注入的lateinit变量测试调用自定义Bean时未初始化问题
问题原因
- 你测试代码中的
RedisSubscriptionStore实例是手动调用构造方法val sut = RedisSubscriptionStore(redisTemplate, objectMapper)创建的,没有交给Spring IoC容器管理。 @Value、@Autowired这类Spring注解的注入逻辑仅对容器创建的Bean生效,自行new的对象不会被Spring做任何属性注入处理,因此defaultPackageForFCMInstance不会被初始化。- 测试类直接加
@Value能拿到值,是因为@SpringBootTest修饰的测试类本身属于Spring容器管理的Bean,Spring会自动处理类内的所有注入注解。
解决方案
方案1:直接注入RedisSubscriptionStore实例(最推荐)
将测试类中手动创建的sut改为Spring自动注入即可,修改后代码如下:
@SpringBootTest() @Tag("integration") internal class RedisSubscriptionStoreIntegTest @Autowired constructor( val objectMapper: ObjectMapper, val redisTemplate: RedisTemplate<String, String>, val sut: RedisSubscriptionStore // 直接注入容器管理的Bean ) { @Test fun `return 200 when updating subscription`() { sut.updateSubscription(updatedSub) //Assert val newlyUpdatedSub = getSubscription(updatedSub.peerID) Assertions.assertEquals(updatedSub, newlyUpdatedSub) } }
该方案拿到的sut和项目正式运行时的实例逻辑完全一致,所有注解都会被正确处理。
方案2:手动给属性赋值
如果确实需要手动创建sut实例,可以把测试类拿到的配置值直接赋值给sut的对应属性:
@SpringBootTest() @Tag("integration") internal class RedisSubscriptionStoreIntegTest @Autowired constructor( val objectMapper: ObjectMapper, val redisTemplate: RedisTemplate<String, String> ) { @Value("${default.package}") lateinit var defaultPackageForFCMInstance: String val sut = RedisSubscriptionStore(redisTemplate, objectMapper) @BeforeEach fun init() { // 手动给sut的属性赋值 sut.defaultPackageForFCMInstance = this.defaultPackageForFCMInstance } }
方案3:手动触发Spring注入逻辑
如果需要模拟Spring的完整注入处理,可以用Spring提供的AutowireCapableBeanFactory处理自行创建的对象:
@SpringBootTest() @Tag("integration") internal class RedisSubscriptionStoreIntegTest @Autowired constructor( val objectMapper: ObjectMapper, val redisTemplate: RedisTemplate<String, String>, val beanFactory: AutowireCapableBeanFactory ) { val sut = RedisSubscriptionStore(redisTemplate, objectMapper) @BeforeEach fun init() { // 触发Spring对手动创建对象的注入逻辑 beanFactory.autowireBean(sut) } }
内容的提问来源于stack exchange,提问作者GeoGoeG
相关产品推荐
相关产品推荐

