1

I am working on the chapter 5 example of the book Spring in Action 4. When I am running the Test classes I found one of the @Valid test is not working, it appears as if the validation never occured(returned status code of 302 and view's name is "/spitter/null"). However, it works fine when I run it in Tomcat.

The validatioon jars(hibernate-validator and its dependencies) are loaded with Maven.

The whole project is on github.

Any ideas?

test code snippet:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes={RootConfig.class, WebConfig.class})
public class SpitterControllerTest {
    @Test
    public void shouldFailValidationWithNoData() throws Exception {
        SpitterRepository mockRepository = mock(SpitterRepository.class);    
        SpitterController controller = new SpitterController(mockRepository);
        MockMvc mockMvc = standaloneSetup(controller).build();

        mockMvc.perform(post("/spitter/register"))
            .andExpect(status().isOk())
            .andExpect(view().name("registerForm"))
            .andExpect(model().errorCount(5))
            .andExpect(model().attributeHasFieldErrors(
                "spitter", "firstName", "lastName", "username", "password", "email"));
    }
}

SpitterController snippet:

  @RequestMapping(value="/register", method=POST)
  public String processRegistration(
      @Valid Spitter spitter, 
      Errors errors) {
    if (errors.hasErrors()) {
      return "registerForm";
    }

    spitterRepository.save(spitter);
    return "redirect:/spitter/" + spitter.getUsername();
  }

Spitter snippet:

@NotNull
  @Size(min=5, max=16)
  private String username;

  @NotNull
  @Size(min=5, max=25)
  private String password;

  @NotNull
  @Size(min=2, max=30)
  private String firstName;

  @NotNull
  @Size(min=2, max=30)
  private String lastName;

  @NotNull
  @Email
  private String email;

Thanks a lot!

Arpit Aggarwal
  • 27,626
  • 16
  • 90
  • 108
fall
  • 984
  • 11
  • 33
  • Is using standaloneSetup required? Have you considered webAppContextSetup instead? – Sean Carroll Jul 08 '17 at 17:29
  • I just tried webAppContextSetup and it threw a ```org.springframework.dao.DataIntegrityViolationException``` because inserting null is not allowed for column username. Still, the ```@Valid``` validation didn't occur. If it's validated, the exception shouldn't have been thrown. – fall Jul 10 '17 at 06:57

3 Answers3

1

I cloned your repository and was able to get the valiation working by updating your jsp-api dependency to 2.2

<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
    <scope>provided</scope>
</dependency>

There are newer versions of that dependency that you might want to take a look at. When using the 2.1 version there were no validators being registered/added to ExtendedServletRequestDataBinder or more precisely the SpringValidatorAdapter targetValidator was null.

The root cause was that the exception below was being thrown in OptionalValidatorFactoryBean.afterPropertiesSet

javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead

I noticed that you updated your shouldFailValidationWithNoData test to use webAppContextSetup but still have a call to mock(SpitterRepository.class) which isn't necessary.

Sean Carroll
  • 2,651
  • 2
  • 24
  • 21
  • Thanks for the webAppContextSetup advice. I tried updating jsp-api and it didn't work on my machine but another solution(https://stackoverflow.com/questions/24386771/javax-validation-validationexception-hv000183-unable-to-load-javax-el-express) to the exception you described solved my problem. I changed the `hibernate-validator` version from `5.4.1.final` to `5.2.4.final` in pom. – fall Jul 12 '17 at 03:03
1

I tried this post's best answer and it worked. I changed the hibernate-validator version from 5.4.1.final to 5.2.4.final in pom. The github project is updated as well.

fall
  • 984
  • 11
  • 33
0

Mark your SpitterController with @Validated

import org.springframework.validation.annotation.Validated;

@Controller
@Validated
@RequestMapping("/spitter")
public class SpitterController {

@Autowired the SpitterController and create MethodValidationPostProcessor bean which delegates to a JSR-303 provider for performing method-level validation on annotated methods, as follows:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { RootConfig.class, WebConfig.class,
        SpitterControllerTest.Config.class })
public class SpitterControllerTest {

    @Autowired
    private SpitterController spitterController;

    @Configuration
    public static class Config {
        @Bean
        public MethodValidationPostProcessor methodValidationPostProcessor() {
            return new MethodValidationPostProcessor();
        }
    }

    @Test
    public void shouldFailValidationWithNoData() throws Exception {
    }

}
Arpit Aggarwal
  • 27,626
  • 16
  • 90
  • 108