2012-09-06 13 views
11

Tengo un POJO llamado navegador que he anotado con anotaciones de Hibernate Validator.¿Cómo obtengo Spring MVC para invocar la validación en una prueba JUnit?

import org.hibernate.validator.constraints.NotEmpty; 

public class Browser { 

    @NotEmpty 
    private String userAgent; 
    @NotEmpty 
    private String browserName; 

... 

} 

He escrito la siguiente prueba de la unidad que intenta verificar mi método controlador captura los errores de validación.

@Test 
public void testInvalidData() throws Exception { 
    Browser browser = new Browser("opera", null); 
    MockHttpServletRequest request = new MockHttpServletRequest(); 

    BindingResult errors = new DataBinder(browser).getBindingResult(); 
    // controller is initialized in @Before method 
    controller.add(browser, errors, request); 
    assertEquals(1, errors.getErrorCount()); 
} 

Aquí método add de mi controlador():

@RequestMapping(value = "/browser/create", method = RequestMethod.POST) 
public String add(@Valid Browser browser, BindingResult result, HttpServletRequest request) throws Exception { 
    if (result.hasErrors()) { 
     request.setAttribute("errorMessage", result.getAllErrors()); 
     return VIEW_NAME; 
    } 

    browserManager.save(browser); 

    request.getSession(false).setAttribute("successMessage", 
      String.format("Browser %s added successfully.", browser.getUserAgent())); 

    return "redirect:/" + VIEW_NAME; 
} 

El problema que estoy experimentando es que resultado nunca tiene errores, así que es como @Valid no está siendo reconocido. Traté de agregar lo siguiente a mi clase de prueba, pero no resuelve el problema.

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration({"file:path-to/WEB-INF/spring-mvc-servlet.xml"}) 

¿Alguien sabe cómo me gustaría obtener @Valid para ser reconocido (y validada) al realizar pruebas con JUnit?

Gracias,

Matt

Respuesta

3

La validación se realiza antes de la llamada al controlador, por lo que su prueba no invoca esta validación.

Hay otro enfoque para probar los controladores, donde no se invoca el controlador directamente. En su lugar, construye y llama a la URL en la que está asignado el controlador. Aquí es un buen ejemplo de cómo hacer esto: http://rstoyanchev.github.com/spring-31-and-mvc-test/#1

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(loader=WebContextLoader.class, locations = {"classpath:/META-INF/spring/applicationContext.xml", "classpath:/META-INF/spring/applicationContext-test-override.xml", "file:src/main/webapp/WEB-INF/spring/webmvc-config.xml"}) 
public class MyControllerTest { 
@Autowired 
WebApplicationContext wac; 
MockMvc mockMvc; 

@Before 
public void setup() { 
    this.mockMvc = MockMvcBuilders.webApplicationContextSetup(this.wac).build(); 
} 

@Test 
@Transactional 
public void testMyController() throws Exception { 
    this.mockMvc.perform(get("/mycontroller/add?param=1").accept(MediaType.TEXT_HTML)) 
    .andExpect(status().isOk()) 
    .andExpect(model().attribute("date_format", "M/d/yy h:mm a")) 
    .andExpect(model().attribute("myvalue", notNullValue())) 
    .andExpect(model().attribute("myvalue", hasSize(2))) 
    .andDo(print()); 
} 
} 

POM (necesidad de utilizar hito repo primavera):

<!-- required for spring-test-mvc --> 
    <repository> 
     <id>spring-maven-milestone</id> 
     <name>Spring Maven Milestone Repository</name> 
     <url>http://maven.springframework.org/milestone</url> 
    </repository> 
... 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-test-mvc</artifactId> 
     <version>1.0.0.M1</version> 
     <scope>test</scope> 
    </dependency> 

NOTA: la lib-mvc-pruebas de la primavera no puede ser de producción todavía. Hay algunas lagunas en la implementación. Creo que está previsto que se implemente por completo para la primavera 3.2.

Este enfoque es una gran idea ya que prueba completamente sus controladores. Es fácil estropear las asignaciones de su controlador, por lo que realmente deben someterse a pruebas unitarias.

2

Los validadores se llaman antes que los métodos del controlador invocados, durante el proceso de vincular la solicitud a los parámetros del método. Dado que en este caso está invocando directamente el método del controlador, los pasos de enlace y validación se están pasando por alto.

La manera de hacerlo funcionar es realizar la llamada al controlador a través de la pila Spring MVC. Hay algunas maneras de hacerlo, creo que la mejor manera es usar spring-test-mvc, que proporciona un buen mecanismo para llamar a través de la pila.

Otra forma de llamar a través de la pila es inyectar en HandlerAdapter a la prueba de la siguiente manera:

@Autowired 
private RequestMappingHandlerAdapter handlerAdapter; 

Luego, en la prueba:

MockHttpServletRequest request = new MockHttpServletRequest("POST","/browser/create"); 
MockHttpServletResponse response = new MockHttpServletResponse(); 
httpRequest.addParameter(....);//whatever is required to create Browser.. 
ModelAndView modelAndView = handlerAdapter.handle(httpRequest, response, handler); 
2

Básicamente instanciado un POJO con this.controller = new MyController(), a continuación, llamado su método this.controller.add(...). Simplemente Java simple con un objeto simple, sin ningún contexto: @Valid no se tiene en cuenta.

@ContextConfiguration solo cargará sus posibles beans, con posibles validadores personalizados y demás, pero no hará la magia de procesar @Valid.

Lo que necesita es algo para emular una solicitud al método add del controlador. Emule completamente, validación incluida. No estuvo lejos de hacerlo, ya que utilizó algunas instalaciones de prueba de Spring (para instanciar una MockHttpServletRequest).

Si utiliza 3.0.x primavera o menos, que tiene que hacer

new AnnotationMethodHandlerAdapter() 
     .handle(request, new MockHttpServletResponse(), this.controller); 

a hacer que funcione.

Si utiliza Spring 3.1+, la solución anterior no funcionará (see this link for more info)! Tendrá que usar this library (del equipo de Spring, para que su sonido no se preocupe), mientras espera que se integre en la próxima versión de Spring. A continuación, tendrá que hacer algo como

myMockController = MockMvcBuilders.standaloneSetup(new MyController()).build(); 
myMockController.perform(get("/browser/create")).andExpect(...); 

también echar un vistazo a estas muy interesting slides de Rossen Stoyanchev (la parte que estamos hablando aquí comienza a diapositiva # 116)!

Nota: No entraré en el debate de si este tipo de prueba se considera o no como pruebas unitarias o pruebas de integración. Algunos dirían que esto es más bien una prueba de integración que estamos haciendo aquí, ya que emulamos la ruta completa de una solicitud. Pero por otro lado, todavía puedes burlar tu controlador con anotaciones @Mock de Mockito (o hacer cosas similares con cualquier otro marco burlón), por lo que otros dirán que puedes reducir el alcance de la prueba a "pruebas unitarias" casi puras. . Por supuesto, puede probar el controlador de manera alternativa y puramente con Java + un marco burlón, pero en este caso no le permitirá probar la validación @Valid. ¡Haz tu elección! :)

Cuestiones relacionadas