如何在非独立运行的Spring Boot Maven子模块中测试Spring Bean及配置属性
问题描述
我的主项目基于Spring Boot与Maven构建,创建了一个新的Maven子模块作为依赖引入主项目,因此子模块无需成为可独立运行的Spring Boot项目。但子模块中使用了部分Spring注解,且拥有自身的配置属性。目前尚未将子模块接入主项目,想完成编码与测试后再集成,但测试时遇到问题:子模块没有真实Spring应用上下文的情况下,是否可以编写测试用例?比如测试类中
@Autowired注入的对象均为null,是不是因为缺少Spring应用上下文导致的?
测试类代码
@ExtendWith(SpringExtension.class) @EnableConfigurationProperties(value = CustomerConfiguration.class) @TestPropertySource("classpath:application-test.properties") public class CustomerServiceTest { @Autowired CustomerService service; // 此处为NULL @Autowired CustomerConfiguration config; // 此处为NULL @Test public void getCustomerTest() { ..some tests service.getCustomer(); } }
服务类代码
@Service public class CustomerService { }
子模块pom依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency>
问题根源
你猜的完全没错!@Autowired注入的对象为null,核心原因就是没有加载有效的Spring应用上下文。虽然你用了@ExtendWith(SpringExtension.class),这个注解只是让测试具备Spring测试的基础能力,但并没有主动扫描你的@ServiceBean和配置类——Spring容器里根本不存在CustomerService和CustomerConfiguration的实例,自然注入失败。
解决方法
针对这种非独立Spring Boot模块的测试,有两种实用方案:
方案一:启动迷你Spring上下文(推荐)
不需要启动完整的Spring Boot应用,只加载测试需要的Bean和配置即可。可以用@SpringBootTest的classes参数指定要加载的类,或者用@ComponentScan限定扫描范围:
修改测试类如下:
@ExtendWith(SpringExtension.class) // 只加载测试需要的类,避免冗余 @SpringBootTest(classes = {CustomerService.class, CustomerConfiguration.class}) @TestPropertySource("classpath:application-test.properties") public class CustomerServiceTest { @Autowired CustomerService service; @Autowired CustomerConfiguration config; @Test public void getCustomerTest() { // 你的测试逻辑 service.getCustomer(); } }
如果你的Bean都在同一个包下,用@ComponentScan更简洁:
@ExtendWith(SpringExtension.class) // 替换成你的子模块实际包路径 @ComponentScan(basePackages = "com.your.submodule.package") @EnableConfigurationProperties(CustomerConfiguration.class) @TestPropertySource("classpath:application-test.properties") public class CustomerServiceTest { // 注入和测试逻辑不变 }
方案二:手动实例化Bean(适合简单场景)
如果你的服务类依赖很少,也可以完全脱离Spring上下文,手动创建对象并绑定配置:
public class CustomerServiceTest { private CustomerService service; private CustomerConfiguration config; @BeforeEach void setUp() { // 初始化配置类并绑定测试属性 config = new CustomerConfiguration(); StandardEnvironment env = new StandardEnvironment(); // 手动添加测试属性,替换成你的配置项 TestPropertySourceUtils.addInlinedPropertiesToEnvironment( env, "customer.name=testUser", "customer.age=25" ); // 触发@ConfigurationProperties的属性绑定 new ConfigurationPropertiesBindingPostProcessor().postProcessBeforeInitialization(config, "customerConfiguration"); // 实例化服务类,若有依赖则手动传入 service = new CustomerService(); // 如果服务类依赖config,通过setter注入 // service.setCustomerConfig(config); } @Test public void getCustomerTest() { service.getCustomer(); } }
这种方式适合快速测试简单Bean,但如果依赖链复杂,手动维护成本会很高。
额外提示
- 你的子模块pom中,
spring-boot-starter-test已经包含了所有Spring测试所需依赖,junit-vintage-engine(JUnit 4)和junit-jupiter-engine(JUnit 5)建议保留其中一个即可,推荐使用JUnit 5的依赖。 - 如果你的配置类使用了
@ConfigurationProperties,记得要么给配置类加@Configuration注解,要么在测试类中用@EnableConfigurationProperties启用属性绑定。
内容的提问来源于stack exchange,提问作者Spring

