SpringBoot3中@EnableMethodSecurity导致@WebMvcTest的@Value解析失败
问题描述
在SpringBoot 3 + JVM 17的微服务中,通过Spring Security的@EnableMethodSecurity实现@PreAuthorize权限控制,但使用@WebMvcTest编写控制器单元测试时,出现@Value解析失败的报错:
Caused by: org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'boolean'; Invalid boolean value [${myFeature.active:true}]
移除配置类中的@EnableMethodSecurity注解后,测试可正常运行。
原因
启用@EnableMethodSecurity时,Spring Security会引入更多自动配置组件,部分组件需要解析配置属性。但@WebMvcTest默认仅加载Web层相关配置,未完整加载环境配置的解析逻辑(如PropertySourcesPlaceholderConfigurer),导致@Value中的占位符未被正确解析,直接将${myFeature.active:true}作为字符串传入,无法转换为boolean类型。
解决方案
方案1:添加占位符解析器Bean
在测试类中添加静态配置类,手动注册PropertySourcesPlaceholderConfigurer,确保占位符被正确解析:
@WebMvcTest(controllers = DocApiController.class) class DocApiControllerTest { @Autowired protected MockMvc mockMvc; @MockBean private DocService service; @TestConfiguration static class TestConfig { @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } } @Test void test_getDocuments_success() throws Exception { Result response = new Result(); when(service.search(any())).thenReturn(response); mockMvc.perform(get("/document/get?pageSize=10&pageNumber=1")) .andExpect(status().isOk()); } }
方案2:直接指定测试属性
在测试类上通过@TestPropertySource直接配置属性值,跳过占位符解析:
@WebMvcTest(controllers = DocApiController.class) @TestPropertySource(properties = {"myFeature.active=true"}) class DocApiControllerTest { @Autowired protected MockMvc mockMvc; @MockBean private DocService service; @Test void test_getDocuments_success() throws Exception { Result response = new Result(); when(service.search(any())).thenReturn(response); mockMvc.perform(get("/document/get?pageSize=10&pageNumber=1")) .andExpect(status().isOk()); } }
方案3:引入必要的配置类
如果需要加载Security配置类,可在@WebMvcTest中指定包含该配置类,确保环境配置逻辑被加载:
@WebMvcTest( controllers = DocApiController.class, includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = config.class) ) class DocApiControllerTest { @Autowired protected MockMvc mockMvc; @MockBean private DocService service; @Test void test_getDocuments_success() throws Exception { Result response = new Result(); when(service.search(any())).thenReturn(response); mockMvc.perform(get("/document/get?pageSize=10&pageNumber=1")) .andExpect(status().isOk()); } }
注:此方式会引入更多Security相关Bean,需确保这些Bean的依赖已被Mock或提供。
内容的提问来源于stack exchange,提问作者David Yuan

