7

In my entities I have some hibernate annotations for validation, like @NotEmpty, @Pattern.. and others

In my controller, on save action, it has an @Valid parameter.

But if any entity has any required field, and there is no annotation I will have problems.

So I would like to test each entity, to ensure they have the necessary notes.

Something like:

@Test(expect=IllegalArgumentException.class)
public void testAllNull() {
    Person p = new Persson(); // Person name has an @NotEmpty
    validator.validate(p);
}

But how to validate it? Who is called to check @Valid?

Thanks.

Falci
  • 1,823
  • 4
  • 29
  • 54

3 Answers3

7

I found out how to check:

    @Autowired
    private LocalValidatorFactoryBean validator;

    ...

    validator.validateProperty(object, propertyName)
Falci
  • 1,823
  • 4
  • 29
  • 54
3

Here is a Spring v4.1.x based example of a test validating presence and processing of the @Valid annotation and building of custom JSON response in case of an error.

jUnit

import com.fasterxml.jackson.core.type.TypeReference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import javax.inject.Inject;
import java.util.List;

import static org.abtechbit.miscboard.util.JsonUtils.toJson;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
        RegistrationValidationTest.MockDependencies.class,
})
public class RegistrationValidationTest {

    @Inject
    MockMvc mvc;

    @Test
    public void validatesRegistration() throws Exception {
        Registration registration = ... //build an invalid Registration object
        MvcResult result = mvc.perform(post(RegistrationController.CONTEXT_REGISTER).
                contentType(MediaType.APPLICATION_JSON).
                content(toJson(registration))).
                andExpect(status().isBadRequest()).
                andExpect(content().contentType(MediaType.APPLICATION_JSON)).
                andReturn();

        assertThat(result.getResolvedException(), is(notNullValue()));
        String content = result.getResponse().getContentAsString();
        assertThat(content, is(notNullValue()));
        List<Message> messages = JsonUtils.fromJson(content, new TypeReference<List<Message>>() {
        });
        assertThat(messages.size(), is(1));
    }

    public static class MockDependencies {

        @Bean
        public MockMvc mvc() {
            return MockMvcBuilders.standaloneSetup(new RegistrationController()).build();
        }
    }
}

Controller

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;
import java.util.stream.Collectors;

@Controller
public class RegistrationController
{
    public static final String CONTEXT_REGISTER = "/register";

    @RequestMapping(value = CONTEXT_REGISTER, method = RequestMethod.POST)
    @ResponseBody
    public String register(@RequestBody @Valid Registration registration) {
        //perform registration
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<List> handleValidationException(MethodArgumentNotValidException ex) {
        //Build a list of custom Message{String message;} objects
        List<Message> messages = ex.getBindingResult().getAllErrors().
                stream().map(e->new Message(e.getDefaultMessage())).collect(Collectors.toList());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_JSON).body(messages);
    }
}
Altair7852
  • 1,226
  • 1
  • 14
  • 23
0

Spring MVC Test Framework might be a good choice. By using this, you can be assured that validations in your tests runs codes as Spring @MVC actually works.

Actually, the @Valid annotation is detected by HandlerMethodInvoker, which processes annotations on the handler methods of Spring controllers. Internally, the actual validation logic is delegated to the Validator bean depending on your application context settings. (Hibernate Validator is widely used.)

By default configuration (e.g. <mvc:annotation-driven />), LocalValidatorFactoryBean is used internally to process @Valid annotation as @Falci noted, but it may differ time to time. Instead, Spring MVC Test Framework provides the same environment as the main application uses, hence a good choice.

Jongwook Choi
  • 8,171
  • 3
  • 25
  • 22