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>2.2.1</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.