UNIQUEorn
UNIQUEorn

Reputation: 418

Spring Boot: How to test a service in JUnit with @Validated annotation?

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

Answers (3)

Le Vu
Le Vu

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

Simon Martinelli
Simon Martinelli

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

Rammgarot
Rammgarot

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

Related Questions