2010-02-22 127 views
9

我有一天遇到了一个问题,那里有一个@Valid注释被意外地从控制器类中删除。不幸的是,它没有破坏我们的任何测试。我们的单元测试都没有实际执行Spring AnnotationMethodHandlerAdapter通路。我们只是直接测试我们的控制器类。测试Spring @MVC注解

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

+1

你不会进行单元测试的注解,你会吗?似乎是我对整合测试的关注。 – 2010-02-23 12:15:32

回答

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带有各种与servlet相关的MockXYZ类,包括MockServletContext,MockHttpServletRequestMockHttpServletResponse。在通常意义上,它们并不是真正的“模拟”对象,它们更像是愚蠢的存根,但它们完成了这项工作。

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

13

我写这种东西的集成测试。假设你有验证注释一个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"));   
    } 

查看integration testing Spring's MVC annotations

+0

这是friggin'真棒。这正是我寻找的东西!谢谢! – 2010-07-29 18:19:30