JUnit测试套件中通过ConditionalOnProperty替换Bean失败问题
你遇到的问题核心是Spring Test的上下文缓存机制在测试套件运行时引发的冲突。单独运行测试时,Spring会创建一个适配a.on=true的ApplicationContext,测试结束后销毁;但在测试套件中,若其他测试先使用了默认配置(a.on=false)的上下文,Spring会缓存这个上下文。当运行你的自定义属性测试时,若上下文缓存键未正确区分属性差异,就会复用旧上下文,导致Bean加载冲突(比如数据源MBean重复注册,以及SharedInterface的实现类可能同时尝试加载)。
解决方案1:用@DirtiesContext强制销毁上下文
在你的那个使用@TestPropertySource(properties = { "a.on=true" })的测试类上添加@DirtiesContext注解,告诉Spring在测试结束后销毁当前上下文,避免被后续测试复用:
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) @TestPropertySource(properties = { "a.on=true" }, inheritLocations = true) public class YourCustomTest { // 你的测试代码 }
- 如果这个测试类里的所有测试方法都用同一属性配置,
AFTER_CLASS模式就足够了;要是不同测试方法需要不同属性,可以换成AFTER_EACH_TEST_METHOD模式。
解决方案2:让上下文缓存识别属性差异
Spring Test默认会把@TestPropertySource里的配置纳入上下文缓存的判断依据,但有时候用@SpringBootTest的properties属性替代@TestPropertySource,能更明确地让Spring为该测试创建独立上下文:
@SpringBootTest(properties = { "a.on=true" }) public class YourCustomTest { // 你的测试代码 }
这种方式会确保Spring将a.on=true作为上下文缓存键的一部分,不会和默认配置的上下文混淆,自然就不会出现冲突了。
异常原因拆解
当测试套件运行时,Spring为了提升性能会尽可能复用已创建的ApplicationContext。如果先运行了用默认a.on=false的测试,Spring会缓存包含B类的上下文;当运行你的a.on=true测试时,若缓存键没区分开属性变化,Spring会尝试复用旧上下文,此时既要加载A类,又要保留已存在的B类和数据源MBean,最终就会抛出InstanceAlreadyExistsException。
内容的提问来源于stack exchange,提问作者rodalfus

