2010-02-22 35 views
9

Me encontré con un problema el otro día cuando una anotación @Valid se eliminó accidentalmente de una clase de controlador. Desafortunadamente, no rompió ninguna de nuestras pruebas. Ninguna de nuestras pruebas unitarias realmente ejerce la ruta Spring AnnotationMethodHandlerAdapter. Simplemente probamos nuestras clases de controlador directamente.Testing Spring @MVC anotaciones

¿Cómo puedo escribir una unidad o una prueba de integración que fallará correctamente si mis anotaciones @MVC son incorrectas? ¿Hay alguna manera en que pueda pedirle a Spring que encuentre y ejercite el controlador relevante con MockHttpServlet o algo así?

+1

No te unidad de probar una anotación, ¿verdad? Parece ser una preocupación de prueba de integración para mí. –

Respuesta

1

en próxima sprin g 3.2 (SNAPSHOT disponible) o con spring-test-mvc (https://github.com/SpringSource/spring-test-mvc) puede hacerlo así:

primero emulamos la validación porque no queremos para probar el validador, solo quiero saber si se llama validación.

public class LocalValidatorFactoryBeanMock extends LocalValidatorFactoryBean 
{ 
    private boolean fakeErrors; 

    public void fakeErrors () 
    { 
     this.fakeErrors = true; 
    } 

    @Override 
    public boolean supports (Class<?> clazz) 
    { 
     return true; 
    } 

    @Override 
    public void validate (Object target, Errors errors, Object... validationHints) 
    { 
     if (fakeErrors) 
     { 
      errors.reject("error"); 
     } 
    } 
} 

esta es nuestra clase de prueba:

@RunWith(SpringJUnit4ClassRunner.class) 
@WebAppConfiguration 
@ContextConfiguration 
public class RegisterControllerTest 
{ 
@Autowired 
private WebApplicationContext wac; 
private MockMvc mockMvc; 

    @Autowired 
    @InjectMocks 
    private RegisterController registerController; 

    @Autowired 
    private LocalValidatorFactoryBeanMock validator; 

    @Before 
    public void setup () 
    { 
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); 
    // if you want to inject mocks into your controller 
      MockitoAnnotations.initMocks(this); 
    } 

    @Test 
    public void testPostValidationError () throws Exception 
    { 
     validator.fakeErrors(); 
     MockHttpServletRequestBuilder post = post("/info/register"); 
     post.param("name", "Bob"); 
     ResultActions result = getMockMvc().perform(post); 
      // no redirect as we have errors 
     result.andExpect(view().name("info/register")); 
    } 

    @Configuration 
    @Import(DispatcherServletConfig.class) 
    static class Config extends WebMvcConfigurerAdapter 
    { 
     @Override 
     public Validator getValidator () 
     { 
      return new LocalValidatorFactoryBeanMock(); 
     } 

     @Bean 
     RegisterController registerController () 
     { 
      return new RegisterController(); 
     } 
    } 
} 
3

Sure. No hay ninguna razón por la cual su prueba no pueda instanciar su propio DispatcherServlet, inyéctelo con los diversos elementos que tendría en un contenedor (por ejemplo, ServletContext), incluida la ubicación del archivo de definición del contexto.

primavera viene con una variedad de clases relacionadas con MockXYZ servlet-para este propósito, incluyendo MockServletContext, MockHttpServletRequest y MockHttpServletResponse. En realidad no son objetos "falsos" en el sentido habitual, son más como tontos, pero hacen el trabajo.

El contexto de prueba del servlet tendría los beans relacionados con MVC habituales, más sus beans para probar. Una vez que se inicialice el servlet, cree las solicitudes y respuestas simuladas, y aliméntelos en el método service() de servet. Si la solicitud se enruta correctamente, puede verificar los resultados tal como están escritos en la respuesta simulada.

13

Escribo pruebas de integración para este tipo de cosas. Digamos que tiene un grano con anotaciones de validación:

public class MyForm { 
    @NotNull 
    private Long myNumber; 

    ... 
} 

y un controlador que maneja la presentación

@Controller 
@RequestMapping("/simple-form") 
public class MyController { 
    private final static String FORM_VIEW = null; 

    @RequestMapping(method = RequestMethod.POST) 
    public String processFormSubmission(@Valid MyForm myForm, 
      BindingResult result) { 
     if (result.hasErrors()) { 
      return FORM_VIEW; 
     } 
     // process the form 
     return "success-view"; 
    } 
} 

y desea probar que el @Valid y anotaciones @NotNull están cableados correctamente:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration({"file:web/WEB-INF/application-context.xml", 
    "file:web/WEB-INF/dispatcher-servlet.xml"}) 
public class MyControllerIntegrationTest { 

    @Inject 
    private ApplicationContext applicationContext; 

    private MockHttpServletRequest request; 
    private MockHttpServletResponse response; 
    private HandlerAdapter handlerAdapter; 

    @Before 
    public void setUp() throws Exception { 
     this.request = new MockHttpServletRequest(); 
     this.response = new MockHttpServletResponse(); 

     this.handlerAdapter = applicationContext.getBean(HandlerAdapter.class); 
    } 

    ModelAndView handle(HttpServletRequest request, HttpServletResponse response) 
      throws Exception { 
     final HandlerMapping handlerMapping = applicationContext.getBean(HandlerMapping.class); 
     final HandlerExecutionChain handler = handlerMapping.getHandler(request); 
     assertNotNull("No handler found for request, check you request mapping", handler); 

     final Object controller = handler.getHandler(); 
     // if you want to override any injected attributes do it here 

     final HandlerInterceptor[] interceptors = 
      handlerMapping.getHandler(request).getInterceptors(); 
     for (HandlerInterceptor interceptor : interceptors) { 
      final boolean carryOn = interceptor.preHandle(request, response, controller); 
      if (!carryOn) { 
       return null; 
      } 
     } 

     final ModelAndView mav = handlerAdapter.handle(request, response, controller); 
     return mav; 
    } 

    @Test 
    public void testProcessFormSubmission() throws Exception { 
     request.setMethod("POST"); 
     request.setRequestURI("/simple-form"); 
     request.setParameter("myNumber", ""); 

     final ModelAndView mav = handle(request, response); 
     // test we're returned back to the form 
     assertViewName(mav, "simple-form"); 
     // make assertions on the errors 
     final BindingResult errors = assertAndReturnModelAttributeOfType(mav, 
       "org.springframework.validation.BindingResult.myForm", 
       BindingResult.class); 
     assertEquals(1, errors.getErrorCount()); 
     assertEquals("", errors.getFieldValue("myNumber"));   
    } 

Ver mi blog el integration testing Spring's MVC annotations

+0

Esto es increíble. ¡Este es exactamente el tipo de cosa que estaba buscando! ¡Gracias! –