Reputation: 418
I'm trying to build a set of constraint validators for my Spring Boot application. I want to build some validation annotations like @NotNull
. Btw: The validations should support validation groups.
So I have a simple item model with a validation annotation:
public class Item {
@NotNull(groups=OnCreate.class) // Not Null on validation group 'OnCreate'
private String mayNotBeNull;
// Constructors and getter/setter stuff.
}
Then I wrapped the persistence logic using a validated service:
@Service
@Validated
public class MyService {
public Item create(@Validated(OnCreate.class) Item item) {
Item savedItem = repository.save(item);
return savedItem;
}
}
Now I want to test this service without starting a full blown MVC test (which would start all REST controllers and stuff I do not need).
I started to write my test:
@ContextConfiguration(classes = {
ItemRepository.class, MyService.class, LocalValidatorFactoryBean.class
})
@RunWith(SpringRunner.class)
public class PlantServiceTest {
@MockBean
private ItemRepository repository;
@Autowired
private MyService service;
@Autowired
private Validator validator;
@Test
public void shouldDetectValidationException() {
// ... building an invalid item
Item item = new Item();
item.setMayNotBeNull(null); // <- This causes the validation exception.
validator.validate(item, OnCreate.class);
}
@Test
public void shouldAlsoDetectValidationException() {
// ... building an invalid item
Item item = new Item();
item.setMayNotBeNull(null); // <- This should cause the validation exception.
service.create(item); // <- No validation error. Service is not validating.
}
}
The method shouldThrowValidationException
detects the validation error, because the field value in item is null
.
The method shouldAlsoDetectValidationException
does not detect the validation error.
I think I missed something when configuring the context. The service object is not extended by the validation logic.
How can I configure the test so that the autowired services are decorated with the validation logic provided by @Validated
?
Upvotes: 5
Views: 11808
Reputation: 91
In my case, I wanna spy validator object, and test the validate().
Rely on @Rammgarot's answer, In order to clarify the problem more clearly, let's say we have: FooService.java
private final Validator validator;
public void validateRequestForExample(Request request) {
validator.validate(request);
}
and your FooServiceTest.java
@InjectMocks FooService fooservice;
@Spy private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
void testValidatorRequestHandling() {
fooService.validateRequestForExample(yourStubRequest);
}
Upvotes: 0
Reputation: 36103
@Validated does not work as expected on Parameters. You have to use @Valid on the parameter and add the @Validated with the group on the method or class level.
This way it works:
@Service
@Validated
public class MyService {
@Validated(OnCreate.class)
public Item create(@Valid Item item) {
...
}
}
Unfortunately I found no way to have the group on the parameter level.
If you want to test your validation logic in a Spring Unit Test, then you must import the ValidationAutoConfiguration class via:
@Import(ValidationAutoConfiguration.class)
Upvotes: 9
Reputation: 1667
You can do it like:
@Spy private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
...
Object object = new...
Method method = %your_class%.class.getMethod("%method name%", param_1.class, ..., param_n.class);
Object[] parameterValues = { 1L, 0, null }; //e.g.
Set<ConstraintViolation<%your_class%>> violations = executableValidator.validateParameters(object, method, parameterValues);
assertEquals(%number_of_violations%, violations.size());
Upvotes: 3