You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在非独立运行的Spring Boot Maven子模块中测试Spring Bean及配置属性

Spring Boot子模块无独立上下文时的测试方案

问题描述

我的主项目基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 16:17:36