Spring Boot测试时被测Service类@Value注入属性返回null
问题描述
我有一个基于Spring Boot 2.7.0版本搭建的项目,针对dev、test环境分别配置了独立的配置文件:application-dev.properties和application-test.properties,项目中未配置默认的application.properties文件。
在被测Service类中,我需要从application-test.properties文件加载指定属性值,该Service类代码如下:
@Service @RequiredArgsConstructor public class FileServiceImpl implements FileService { @Value("${files.root-directory}") private String fileDirectory; @Override @Transactional(readOnly = false) public File createFile(CreateFileCommand command) { var filePath = FileUtil.getPath(fileDirectory); // 测试场景下fileDirectory为null // .... } }
使用dev profile启动应用时所有功能运行正常,但测试场景下fileDirectory属性始终为null。
对应测试类代码如下:
@SpringBootTest @ActiveProfiles("test") @ExtendWith(MockitoExtension.class) public class FileServiceTest { // ... }
应用启动测试类代码如下:
@SpringBootTest @ActiveProfiles("test") @TestPropertySource("classpath:application-test.properties") @EnableConfigurationProperties class ApiApplicationTests { @Test void contextLoads() { } }
application-dev.properties文件配置内容:
files.root-directory=${user.home}\\api\\files
application-test.properties文件配置内容(原文笔误标注为application-dev.properties):
files.root-directory=/home/api/var/api/files
项目文件结构截图:
问题原因
核心是测试类的注解使用和Bean加载方式不符合Spring Boot测试逻辑,具体两点:
- 注解混用导致Spring属性注入不生效:
@ExtendWith(MockitoExtension.class)是纯Mockito的JUnit5扩展,本身不会启动Spring容器,也不会处理Spring配置加载、属性注入、Bean生命周期逻辑。如果和@SpringBootTest同时标注,且测试类中用@InjectMocks+@Mock的纯Mockito方式实例化Service,得到的是Mockito反射创建的对象,完全脱离Spring容器管理,@Value的属性注入逻辑根本不会执行,字段自然保持null值。 - 多余注解干扰配置加载:启动测试类上的
@TestPropertySource("classpath:application-test.properties")和@EnableConfigurationProperties都是冗余配置。只要正确指定@ActiveProfiles("test"),Spring Boot会自动加载对应profile的配置文件,手动重复指定反而可能因属性源优先级问题覆盖正常加载逻辑。另外未配置默认application.properties本身不会导致配置加载失败,不是问题诱因。
解决方案
根据测试场景选择对应方案即可:
方案1:需要完整Spring上下文的集成测试
- 移除
FileServiceTest上的@ExtendWith(MockitoExtension.class)注解。@SpringBootTest已经自带Spring对JUnit5的扩展支持,会自动启动Spring容器、加载对应profile配置、完成属性注入。如果需要Mock容器中的其他依赖Bean,直接用Spring Boot自带的@MockBean注解即可,不需要纯Mockito扩展。
修改后的测试类示例:@SpringBootTest @ActiveProfiles("test") public class FileServiceTest { // 直接注入Spring管理的Service实例 @Autowired private FileService fileService; // 需要Mock依赖时直接用@MockBean // @MockBean // private OtherDependency otherDependency; } - 移除
ApiApplicationTests上多余的@TestPropertySource和@EnableConfigurationProperties注解,修改后代码如下:@SpringBootTest @ActiveProfiles("test") class ApiApplicationTests { @Test void contextLoads() { } } - 确认
application-test.properties存放在测试资源目录src/test/resources下,路径和主资源目录的配置文件路径一致,保证测试运行时能被正确扫描。
方案2:不需要启动Spring容器的轻量单元测试
如果不需要加载完整Spring上下文,直接去掉@SpringBootTest相关注解,纯Mockito场景下手动给属性注入测试值即可:
@ExtendWith(MockitoExtension.class) public class FileServiceTest { @InjectMocks private FileServiceImpl fileService; @Mock private OtherDependency otherDependency; @BeforeEach void setUp() { // 通过反射给@Value标注的字段注入测试值 ReflectionTestUtils.setField(fileService, "fileDirectory", "/test/path/files"); } // 测试用例 }
内容的提问来源于stack exchange,提问作者Ali Behzadian Nejad
相关产品推荐
相关产品推荐

