Spring Boot测试中如何避免其他测试配置类泄露?
首先得戳破你遇到的问题根源:@TestConfiguration本质是@Component的派生注解,如果你的主配置(MyConfiguration)所在的包被组件扫描覆盖(比如项目根包用了@SpringBootApplication,默认扫描根包及其子包),那么SpringConfigurationTest里的静态内部测试配置类会被自动扫描加载,哪怕你没在AnotherSpringConfigurationTest的@ContextConfiguration里指定它。
给你几个简洁优雅的解决办法,都是围绕「明确指定所需配置、避免自动扫描泄露」的思路:
方案1:把测试配置改成普通@Configuration,切断自动扫描路径
直接把测试类里的@TestConfiguration换成普通的@Configuration——普通@Configuration不属于@Component体系,不会被组件扫描自动发现,只有你显式在@ContextConfiguration里指定才会生效。
修改SpringConfigurationTest的配置:
package com.example; @RunWith(SpringRunner.class) @ContextConfiguration(classes = {MyConfiguration.class, SpringConfigurationTest.MyTestConfiguration.class}) public class SpringConfigurationTest { // 替换@TestConfiguration为普通@Configuration @Configuration static class MyTestConfiguration { @Bean @Primary public A beanA() { return mock(A.class); } } }
这样AnotherSpringConfigurationTest的上下文只会加载你指定的MyConfiguration和自己的测试配置,完全不会触发其他测试的配置类。
方案2:给测试配置加条件限制,仅在当前测试生效
如果你不想放弃@TestConfiguration,可以给测试配置类加上@ConditionalOnClass,指定只有当前测试类存在时才激活这个配置:
package com.example; @RunWith(SpringRunner.class) @ContextConfiguration(classes = {MyConfiguration.class, SpringConfigurationTest.MyTestConfiguration.class}) public class SpringConfigurationTest { @TestConfiguration // 只有当前测试类在上下文里才生效 @ConditionalOnClass(SpringConfigurationTest.class) static class MyTestConfiguration { @Bean @Primary public A beanA() { return mock(A.class); } } }
AnotherSpringConfigurationTest的测试上下文不会加载SpringConfigurationTest类,所以这个带条件的测试配置就不会被激活,完美避免泄露。
方案3:用@Import统一管理配置依赖,让上下文更清晰
把主配置通过@Import导入到测试配置里,而不是在@ContextConfiguration里列多个类,这样每个测试的配置依赖更明确,也能减少扫描泄露的可能:
比如SpringConfigurationTest可以改成:
package com.example; @RunWith(SpringRunner.class) // 只指定当前测试的配置类 @ContextConfiguration(classes = SpringConfigurationTest.MyTestConfiguration.class) public class SpringConfigurationTest { @TestConfiguration // 在这里明确导入主配置 @Import(MyConfiguration.class) static class MyTestConfiguration { @Bean @Primary public A beanA() { return mock(A.class); } } }
AnotherSpringConfigurationTest同理:
package com.example.sub; @RunWith(SpringRunner.class) @ContextConfiguration(classes = AnotherSpringConfigurationTest.MyTestConfiguration.class) public class AnotherSpringConfigurationTest { @TestConfiguration @Import(MyConfiguration.class) static class MyTestConfiguration { @Bean public B beanB() { return new B(); } } }
这个方案的好处是每个测试的配置依赖一目了然,而且如果主配置的组件扫描范围没有覆盖测试包的话,完全不会有泄露问题。
内容的提问来源于stack exchange,提问作者hotzst

