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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 16:32:46