@SpringBootTest集成测试替换Clock Bean报BeanDefinitionOverrideException如何解决
异常原因
Spring Boot 2.1+ 版本默认将spring.main.allow-bean-definition-overriding配置为false,禁止同名称Bean的覆盖,避免非预期的Bean替换引发线上问题。
而@TestConfiguration的核心作用是为测试场景补充额外的Bean配置,默认不会自动覆盖已存在的同名称Bean,你在测试类中定义的clock Bean和生产配置类中的clock Bean名称、类型完全一致,上下文加载时检测到重复的Bean定义,就会抛出BeanDefinitionOverrideException。
解决方案
以下方案按推荐优先级排序:
- 方案1:给测试Bean添加
@Primary注解
在测试配置类的clockBean上添加@Primary,Spring在存在多个同类型Bean时,会优先使用标注了@Primary的Bean,不需要修改全局配置,侵入性最低。
代码示例:
@TestConfiguration static class ClockTestConfiguration { @Bean @Primary public Clock clock() { return Clock.fixed(Instant.ofEpochMilli(1635513004000L), ZoneId.systemDefault()); } }
- 方案2:给生产Bean指定环境排除测试场景
在生产的Clock配置类的Bean上添加@Profile("!test"),测试环境下生产的Clock Bean不会被注册到容器中,自然不会出现冲突:
@Configuration public class ClockConfiguration { @Bean @Profile("!test") Clock clock() { return Clock.systemDefaultZone(); } }
- 方案3:开启全局Bean覆盖配置
在application-test.properties(或yml)中添加如下配置,允许测试环境下Bean覆盖:
spring.main.allow-bean-definition-overriding=true
该方案属于全局配置,可能会引发其他非预期的Bean覆盖问题,优先级低于前两种。
- 方案4:使用
@MockBean直接替换原有Bean
不需要额外写测试配置类,直接在测试类中声明@MockBean替换容器中原有的Clock实例,再指定返回逻辑即可:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ContextConfiguration(initializers = WireMockInitializer.class) @AutoConfigureWebTestClient @ActiveProfiles("test") class MyIT { @MockBean private Clock clock; @BeforeEach void setupClock() { Clock fixedClock = Clock.fixed(Instant.ofEpochMilli(1635513004000L), ZoneId.systemDefault()); Mockito.when(clock.instant()).thenReturn(fixedClock.instant()); Mockito.when(clock.getZone()).thenReturn(fixedClock.getZone()); } // 其他测试代码 }
内容的提问来源于stack exchange,提问作者Robert Strauch
相关产品推荐
相关产品推荐

