如何在Spring Boot单元测试中正确读取测试属性文件
问题解决步骤
1. 修正测试配置文件的属性值
首先,application-test.properties中的属性值不需要加引号,Spring会将引号视为值的一部分,导致后续判断逻辑不符合预期。修改后的配置如下:
spring.security.oauth2.client.registration.cognito.clientId=testClientId spring.security.oauth2.client.registration.cognito.clientSecret=testClientSecret
2. 让Spring容器管理SecurityConfiguration实例
你手动new SecurityConfiguration()的方式会绕过Spring的依赖注入机制,@Value注解完全不生效,导致成员变量为null触发空指针异常。需要让测试上下文加载该配置类,并通过Spring容器注入实例。
3. 重构单元测试类
使用@ContextConfiguration或@SpringBootTest加载配置类,结合@Autowired注入SecurityConfiguration,同时正确模拟HttpSecurity的链式调用(因为HttpSecurity的方法是链式调用,需要mock每个步骤的返回值):
@ContextConfiguration(classes = SecurityConfiguration.class) @TestPropertySource("classpath:application-test.properties") @ExtendWith(SpringExtension.class) public class SecurityConfigurationTest { @Autowired private SecurityConfiguration securityConfiguration; @Test void filterChain_WithValidCognitoConfigs_ShouldApplySecurityRules() throws Exception { // Mock HttpSecurity及其链式调用的各个环节 HttpSecurity httpMock = mock(HttpSecurity.class); CsrfConfigurer<HttpSecurity> csrfConfigurerMock = mock(CsrfConfigurer.class); AuthorizeRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authzMock = mock(AuthorizeRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry.class); OAuth2LoginConfigurer<HttpSecurity> oauth2LoginMock = mock(OAuth2LoginConfigurer.class); LogoutConfigurer<HttpSecurity> logoutMock = mock(LogoutConfigurer.class); // 模拟链式调用的返回值 when(httpMock.csrf()).thenReturn(csrfConfigurerMock); when(csrfConfigurerMock.and()).thenReturn(httpMock); when(httpMock.authorizeRequests()).thenReturn(authzMock); when(authzMock.anyRequest().authenticated()).thenReturn(authzMock); when(authzMock.and()).thenReturn(httpMock); when(httpMock.oauth2Login()).thenReturn(oauth2LoginMock); when(oauth2LoginMock.and()).thenReturn(httpMock); when(httpMock.logout()).thenReturn(logoutMock); when(logoutMock.logoutSuccessUrl("/")).thenReturn(logoutMock); when(httpMock.build()).thenReturn(mock(SecurityFilterChain.class)); // 执行测试方法 SecurityFilterChain filterChain = securityConfiguration.filterChain(httpMock); // 断言与验证 assertNotNull(filterChain); verify(httpMock).csrf(); verify(authzMock).anyRequest().authenticated(); verify(logoutMock).logoutSuccessUrl("/"); } @Test void filterChain_WithoutCognitoConfigs_ShouldPermitAll() throws Exception { // 可以通过覆盖属性值模拟空配置场景 try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext()) { EnvironmentTestUtils.addEnvironment(context, "spring.security.oauth2.client.registration.cognito.clientId=", "spring.security.oauth2.client.registration.cognito.clientSecret="); context.register(SecurityConfiguration.class); context.refresh(); SecurityConfiguration config = context.getBean(SecurityConfiguration.class); HttpSecurity httpMock = mock(HttpSecurity.class); when(httpMock.antMatcher("/**")).thenReturn(httpMock); when(httpMock.build()).thenReturn(mock(SecurityFilterChain.class)); SecurityFilterChain filterChain = config.filterChain(httpMock); assertNotNull(filterChain); verify(httpMock).antMatcher("/**"); } } }
关键原因说明
@Value注解只有在Spring容器管理的Bean实例上才会生效,手动new的对象无法触发属性注入。@WebMvcTest(SendMessageController.class)是针对Web层的切片测试,只会加载与指定Controller相关的Bean,不会加载SecurityConfiguration这类全局配置类。- 测试配置文件中的引号会导致属性值包含多余的引号,可能影响
isBlank()的判断结果。
内容的提问来源于stack exchange,提问作者Vismark Juarez
相关产品推荐
相关产品推荐

