Testing Spring MVC form validations using a tool for MockMvcRequestBuilder

Spring MVC Test Framework is great to test controllers without even running a Servlet container. However, it’s not always straightforward when dealing with form validation, especially when your forms have a lot of properties. This post will show you how to test your form validations easily using a tool that allows Spring's MockMvcRequestBuilder to post an entire form object to a controller.

Validating forms with @Valid annotation

A quick recap about JSR-303 support in Spring MVC.

The @Valid annotation tells Spring MVC to trigger validation on the annotated bean once a request is made:

    @PostMapping("/add")
    public String addUser(@Valid AddUserForm addUserForm, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
        if (bindingResult.hasErrors()) {
            return ADD_USER_VIEW;
        } else { // Save new user 
            redirectAttributes.addFlashAttribute("flash", "User added");
            return "redirect:" + ADD_USER_URL;
        }
    }
public class AddUserForm {  
    @NotNull
    @Size(min = 1)
    private List firstNames;
    @NotNull
    @Size(min = 3)
    private String name;
    @NotNull
    private LocalDate birthDate;
    @NotNull
    @Valid
    private Address address;
    @NotNull
    @Size(min = 1)
    private String[] hobbies;
    @NotNull
    private Gender gender;
}

Submitting form objects with MockMvc

Although MockMvc is a useful tool, it is not so convenient when dealing with huge forms submission. To test a form containing a lot of fields, you have to map each one to an HTTP parameter like this:

this.mockMvc.perform(MockMvcRequestBuilders.post("url")  
    .param("field", "fieldValue")
    .param("field2.nestedField", "nestedFieldValue");

This method can be used if the form doesn’t contain too many fields (or nested ones!). However, it makes things more complicated as it’s error prone (field name, missing field, etc.). It’s also repetitive if you have multiples forms validations to test.

This is why I built a tool for MockMVCRequestBuilder (https://github.com/f-lopes/spring-mvc-test-utils).

Note: A better approach would be to reduce the number of fields in your form. If you can’t do it for some reasons, you could still use this tool.

Sending form objects using a custom MockMvcRequestBuilder

This tool allows sending an entire form object using MockMvcRequestBuilder.

The usage is straightforward:

final AddUserForm addUserForm = new AddUserForm(Arrays.asList("John", "Jack"), "Doe",  
        LocalDate.now(), new Address(1, "Amber", "New York"));
this.mockMvc.perform(MockMvcRequestBuilderUtils.postForm("/users/add", addUserForm))  
    .andExpect(MockMvcResultMatchers.model().attributeErrorCount("addUserForm", 1));

This builder finds fields using reflection and pass them to the request as HTTP parameters:

firstNames[0]=John  
firstNames[1]=Jack  
lastName=Doe  
address.streetNumber=1  
address.street=Amber  
address.city=New York  

It also supports property editors to format the fields the way you want:

MockMvcRequestBuilderUtils.registerPropertyEditor(LocalDate.class, new CustomLocalDatePropertyEditor("dd/mm/yyyy");  

Get the tool

Add these lines to your pom.xml:

<dependency>  
    <groupId>io.florianlopes</groupId>
    <artifactId>spring-mvc-test-utils</artifactId>
    <version>1.0.0</version>
</dependency>  

See the documentation for more info: https://github.com/f-lopes/spring-mvc-test-utils.
The example code for this post is available here: https://github.com/f-lopes/spring-mvc-form-validation-tests.