Beri
Beri

Reputation: 11610

Spring 5 MVC test custom validator

I have custom validator that uses a service

public class ProductCodeValidator implements ConstraintValidator<ProdutCode, Set<String>> {

    ...
    @Override
    public boolean isValid(Set<String> pkdCodes, ConstraintValidatorContext context) {
        return pkdCodes != null && service.count(pkdCodes) == pkdCodes.size();
    }

My goal is to use it inside of a controller MVC test, with a mocked service, to control validation output.

In order to test it within a controller request, I tried to use @WebMvcTest. But then security pops in, giving me 403 everywhere. In order to disable it I added:

@AutoConfigureMockMvc(addFilters = false) // to avoid security
@WebMvcTest(ProdutDataController.class)
class EditCompanyRegistryDataControllerTest {

    @MockBean
    private ProductService pkdService;
 
    @Autowired
    private MockMvc mvc;

But now Spring ignores also form validation, returning status 200/201 instead of 400, when validation should be triggered.

Question:

1 Is there a way to disable security only, without the validation in MVC tests?.

2 Is it possible to register custom validators using MockMvcBuilders.standaloneSetup(...) builder? This way I could ignore spring context, and focus on standalone setup with mocks.

Upvotes: 0

Views: 1614

Answers (1)

Rob Winch
Rob Winch

Reputation: 21720

Use Spring Security Test Support

There really isn't a good way to disable just Spring Security because that is not recommend. Instead it is recommended to use Spring Security's MockMvc test support. This ensures that your tests are aligned with your configuration.

With Spring Boot, the integration with Spring Security is automatically added to the @Autowired MockMvc instance. Then you have the choice of using either Annotations or RequestProcessor to run your test.

Using Annotations:

@Test
@WithMockUser
public void requestProtectedUrlWithUser() throws Exception {
    mvc
        .perform(get("/"))
        ...
}

This will run as the user with username "user" and role "ROLE_USER". You can also override the attributes with something like: @WithMockUser(username="rob", roles="ADMIN").

Alternatively, you can also use a RequestPostProcessor.

mvc
    .perform(get("/").with(user("user")))

You can also override the default roles and password:

mvc
    .perform(get("/admin").with(user("admin").password("pass").roles("USER","ADMIN")))

For more details including additional customization, best practices, etc refer to the links I provided.

Standalone Setup

The standalone setup doesn't configure anything automatically (including validation and security). You an explicitly configure the validator, but you must ensure it is the same instance as that is configured in your application.

One way to do this is to @Autowire the Validator instance.

@SpringBootTest
class TheTest {

    @Autowired
    Validator validator;

    @Test
    void standaloneTest() {
        MockMvc mvc = MockMvcBuilders
                .standaloneSetup(new MyController())
                .setValidator(this.validator).build();
    }

Alternatively, you can create your own instance of the Validator directly. This complicates things as you must keep this in sync with how your Validator is created in Spring. The default is to use OptionalValidatorFactoryBean:

MockMvc mvc = MockMvcBuilders
            .standaloneSetup(new MyController())
            .setValidator(new OptionalValidatorFactoryBean())
            .build();

Upvotes: 1

Related Questions