HOW-TO: Method-level validation in Spring with @Validated annotation
@Validated is a Spring’s specific variant of JSR-303’s javax.validation.Valid, supporting the specification of validation groups. @Validated can be used with Spring MVC handler methods arguments as well as with method level validation.
In this article, you will learn how to use @Validated annotation with method level validation in Spring.
Note: This blog post got a solid update (30/03/2016). The source code got migrated to new repository and it is Spring Boot driven. See references. If you still prefer classic Spring MVC application you may use my spring-mvc-quickstart-archetype
In order to indicate that a specific class is supposed to be validated at the method level it needs to be annotated with @Validated annotation at type level. Methods applicable for validation must have JSR-303 constraint annotations on their parameters and/or on their return values.
@Service
@Validated
public class SomeService {
@Length(min = 3, max = 5)
public String createUser(@NotBlank @Email String email,
@NotBlank String username,
@NotBlank String password) {
return username;
}
}
Beans annotated with @Validated
annotation will be detected by MethodValidationPostProcessor
and validation functionality is delegated to JSR 303 provider. When the validation fails ConstraintViolationException
, with a set of constraint violations, is thrown.
To visualize that let’s create a Unit Test.
@ContextConfiguration(classes = {SomeServiceTest.Config.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class SomeServiceTest {
}
The context of the test is configured using MethodValidationPostProcessor
. This is a must-have configuration in order to trigger method-level validation.
@Configuration
public static class Config {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
@Bean
public SomeService userCreateService() {
return new SomeService();
}
}
The expected exception is verified with AssertJ.
Note: If you want to learn how to verify exceptions in JUnit tests with AssertJ have a look at JUnit: Testing Exceptions with Java 8 and AssertJ.
@Test
public void throwsViolationExceptionWhenAllArgumentsInvalid() {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> service.createUser(null, null, null))
.matches(e -> e.getConstraintViolations().size() == 3);
}
@Test
public void throwsViolationExceptionWhen2ArgumentsInvalid() {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> service.createUser(null, null, "valid"))
.matches(e -> e.getConstraintViolations().size() == 2);
}
@Test
public void throwsViolationExceptionWhenEmailInvalidArgumentsInvalid() {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> service.createUser("invalid_email", "valid", "valid"))
.matches(e -> e.getConstraintViolations().size() == 1)
.matches(e -> e.getConstraintViolations().stream()
.allMatch(v -> v.getMessage().equals("not a well-formed email address")));
}
@Test
public void throwsViolationExceptionWhenReturnValueTooLong() {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> service.createUser("user@domain.com", "too_long_username", "valid"))
.matches(e -> e.getConstraintViolations().size() == 1)
.matches(e -> e.getConstraintViolations().stream()
.allMatch(v -> v.getMessage().equals("length must be between 3 and 5")));
}
@Test
public void createsUser() {
service.createUser("user@domain.com", "valid", "valid");
}
Of course, I could be more specific with the assertions, but the tests are here only to visualize the validation.
Note: You may want to learn how to utilize custom assertions with AssertJ: Spice up your test code with custom assertions
Summary
With just couple lines of code you may start with method-level validation in Spring. But there is one pending question: “Do we want to utilize method-level validation in production code?”. What’s your opinion?
Source code
The source code contains not only the examples from this article, but many others. And it gets updated! See all the examples here: https://github.com/kolorobot/spring-mvc-beanvalidation11-demo
Similar articles
In case you find this article interesting, have a look at my other blog posts: