2010-02-22 2 views
9

На днях я столкнулся с проблемой, когда аннота @Valid была случайно удалена из класса контроллера. К сожалению, он не нарушил ни одного из наших тестов. Ни один из наших модульных тестов фактически не использует путь весны AnnotationMethodHandlerAdapter. Мы просто проверяем классы контроллера напрямую.Тестирование Spring @MVC аннотации

Как я могу написать тест на единицу или интеграцию, который будет работать неправильно, если мои аннотации @MVC неверны? Есть ли способ, которым я могу попросить Spring найти и использовать соответствующий контроллер с помощью MockHttpServlet или что-то еще?

+1

Вы не модульное тестирование аннотации, не так ли? Кажется, это проблема интеграции. –

ответ

1

В предстоящем Sprin g 3.2 (доступно SNAPSHOT) или с помощью теста spring-test-mvc (https://github.com/SpringSource/spring-test-mvc) вы можете сделать это следующим образом:

сначала мы эмулируем проверку, поскольку мы не хотим для проверки валидатора просто нужно знать, вызвана ли проверка.

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"); 
     } 
    } 
} 

это наш тестовый класс:

@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

Несомненно. Нет причин, по которым ваш тест не может создать свой собственный файл DispatcherServlet, ввести его в различные элементы, которые он будет иметь в контейнере (например, ServletContext), включая местоположение файла определения контекста.

Spring поставляется с различными сервлетов связанных MockXYZ классов для этой цели, в том числе MockServletContext, MockHttpServletRequest и MockHttpServletResponse. Они не являются действительно «макетными» объектами в обычном смысле, они больше похожи на тупые заглушки, но они выполняют эту работу.

Контекст тестового сервлета будет иметь обычные компоненты, связанные с MVC, плюс ваши компоненты для тестирования. После того, как сервлет инициализирован, создайте ложные запросы и ответы и подайте их в метод service() сервлета. Если запрос маршрутизируется правильно, вы можете проверить результаты как написано на макет ответа.

13

Я пишу интеграционные тесты для такого рода вещей. Скажем, у вас есть фасоль с аннотациями проверки:

public class MyForm { 
    @NotNull 
    private Long myNumber; 

    ... 
} 

и контроллер, который обрабатывает представления

@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"; 
    } 
} 

и вы хотите проверить, что @Valid и @NotNull аннотаций подключены правильно:

@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"));   
    } 

Смотрите мой блог на integration testing Spring's MVC annotations

+0

Это круто. Это именно то, что я искал! Благодаря! –

Смежные вопросы