测试Spring @MVC注释

前几天我遇到了一个问题,其中一个@Valid注释被意外地从控制器类中删除了。 不幸的是,它没有打破我们的任何测试。 我们的unit testing都没有实际运行Spring AnnotationMethodHandlerAdapter路径。 我们直接测试我们的控制器类。

如果我的@MVC注释错误,我该如何编写一个正确失败的单元或集成测试? 有没有办法我可以让Spring用MockHttpServlet找到并运用相关的控制器?

在即将到来的春季3.2(SNAPSHOT可用)或spring-test-mvc(https://github.com/SpringSource/spring-test-mvc)中你可以这样做:

首先我们模拟validation,因为我们不想测试validation器,只想知道validation是否被调用。

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

我为这种事写了集成测试。 假设你有一个带有validation注释的bean:

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

请参阅我关于集成测试Spring的MVC注释的博客文章

当然。 没有理由为什么你的测试不能实例化它自己的DispatcherServlet ,用它在容器中的各种项目(例如ServletContext )注入它,包括上下文定义文件的位置。

Spring为此提供了各种与servlet相关的MockXYZ类,包括MockServletContextMockHttpServletRequestMockHttpServletResponse 。 它们不是通常意义上的“模拟”对象,它们更像是笨拙的存根,但它们可以完成这项工作。

servlet的测试上下文将包含通常的MVC相关bean,以及要测试的bean。 初始化servlet后,创建模拟请求和响应,并将它们提供给servet的service()方法。 如果请求被正确路由,您可以检查写入模拟响应的结果。