2010-02-22 85 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