Spring 5 MVC测试:禁用安全保留验证及自定义验证器注册问题
问题解答
针对你遇到的两个问题,我来逐一给出实用的解决方案:
1. 在MVC测试中能否仅禁用安全而保留验证功能?
当然可以!你之前用@AutoConfigureMockMvc(addFilters = false)的问题在于,这个配置会一刀切禁用所有Servlet过滤器——虽然规避了Spring Security的拦截,但也意外打乱了Spring MVC验证的自动配置逻辑(更关键的是,@WebMvcTest默认只会扫描指定控制器所在的包,你的自定义ProductCodeValidator很可能根本没被加载到测试上下文里)。
正确的姿势是:Mock Spring Security的过滤器链,而非禁用所有过滤器,同时确保自定义验证器能被测试上下文加载。
解决方案代码:
// 无需addFilters=false,改用Mock SecurityFilterChain跳过安全拦截 @WebMvcTest(ProdutDataController.class) class EditCompanyRegistryDataControllerTest { @MockBean private ProductService pkdService; // Mock Spring Security的过滤器链,直接跳过安全校验 @MockBean private SecurityFilterChain securityFilterChain; // 如果自定义验证器不在控制器所在包下,手动导入它 @Import(ProductCodeValidator.class) @Autowired private MockMvc mvc; // 测试示例:验证失败时返回400 @Test void testInvalidProductCodeReturns400() throws Exception { // 模拟服务返回的数量与传入的不一致(触发验证失败) when(pkdService.count(anySet())).thenReturn(0); mvc.perform(post("/your-endpoint") .contentType(MediaType.APPLICATION_JSON) .content("{\"pkdCodes\": [\"invalid-code\"]}")) .andExpect(status().isBadRequest()); } }
这样配置后,Spring Security的拦截会被跳过,同时Spring MVC的验证机制(包括你的自定义验证器)会正常工作,验证失败时会正确返回400状态码。
2. 是否可以通过MockMvcBuilders.standaloneSetup(...)注册自定义验证器,脱离Spring上下文专注于Mock独立配置?
完全可以!standaloneSetup就是为这种「脱离完整Spring上下文,手动控制组件依赖」的场景设计的,非常适合专注于控制器和验证逻辑的轻量单元测试。
解决方案代码:
class EditCompanyRegistryDataControllerTest { // 手动Mock依赖服务 private final ProductService pkdService = Mockito.mock(ProductService.class); // 初始化自定义验证器并注入依赖 private final ProductCodeValidator productCodeValidator = new ProductCodeValidator(); // 手动创建控制器实例并注入依赖 private final ProdutDataController controller = new ProdutDataController(pkdService); private MockMvc mvc; @BeforeEach void setUp() { // 给自定义验证器注入依赖服务(假设验证器有setter方法,或用构造注入) productCodeValidator.setService(pkdService); // 构建Validator,手动注册你的自定义验证器 ValidatorFactory validatorFactory = Validation.byDefaultProvider() .configure() .constraintValidatorFactory(new ConstraintValidatorFactory() { @Override public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> validatorClass) { // 当需要自定义验证器时,返回已初始化好的实例 if (validatorClass == ProductCodeValidator.class) { return (T) productCodeValidator; } // 其他验证器用默认工厂创建 return new DefaultConstraintValidatorFactory().getInstance(validatorClass); } }) .buildValidatorFactory(); Validator validator = validatorFactory.getValidator(); // 用standaloneSetup构建MockMvc,绑定控制器和自定义Validator mvc = MockMvcBuilders.standaloneSetup(controller) .setValidator(validator) .build(); } // 测试示例:验证通过时返回200 @Test void testValidProductCodeReturns200() throws Exception { Set<String> validCodes = Set.of("valid-code-1", "valid-code-2"); // 模拟服务返回数量与传入的一致(验证通过) when(pkdService.count(validCodes)).thenReturn(validCodes.size()); mvc.perform(post("/your-endpoint") .contentType(MediaType.APPLICATION_JSON) .content("{\"pkdCodes\": [\"valid-code-1\", \"valid-code-2\"]}")) .andExpect(status().isOk()); } }
这种方式完全脱离Spring自动配置,所有组件(控制器、验证器、服务)都由你手动初始化和绑定,测试更轻量、可控,能快速验证控制器与自定义验证器的逻辑。
内容的提问来源于stack exchange,提问作者Beri
相关产品推荐
相关产品推荐

