2012-09-06 83 views
11

我有一個名爲Browser的POJO,我用Hibernate Validator註解進行了註釋。如何讓Spring MVC在JUnit測試中調用驗證?

import org.hibernate.validator.constraints.NotEmpty; 

public class Browser { 

    @NotEmpty 
    private String userAgent; 
    @NotEmpty 
    private String browserName; 

... 

} 

我寫了下面的單元測試,試圖驗證我的Controller方法捕獲驗證錯誤。

@Test 
public void testInvalidData() throws Exception { 
    Browser browser = new Browser("opera", null); 
    MockHttpServletRequest request = new MockHttpServletRequest(); 

    BindingResult errors = new DataBinder(browser).getBindingResult(); 
    // controller is initialized in @Before method 
    controller.add(browser, errors, request); 
    assertEquals(1, errors.getErrorCount()); 
} 

這裏是我的控制器的add()方法:

@RequestMapping(value = "/browser/create", method = RequestMethod.POST) 
public String add(@Valid Browser browser, BindingResult result, HttpServletRequest request) throws Exception { 
    if (result.hasErrors()) { 
     request.setAttribute("errorMessage", result.getAllErrors()); 
     return VIEW_NAME; 
    } 

    browserManager.save(browser); 

    request.getSession(false).setAttribute("successMessage", 
      String.format("Browser %s added successfully.", browser.getUserAgent())); 

    return "redirect:/" + VIEW_NAME; 
} 

我遇到的問題是,結果不會有錯誤,所以它就像是沒有得到認可@Valid。我嘗試將以下內容添加到測試類中,但它不能解決問題。

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration({"file:path-to/WEB-INF/spring-mvc-servlet.xml"}) 

有沒有人知道如何在使用JUnit進行測試時識別@Valid以進行識別(和驗證)?

感謝,

馬特

回答

3

的驗證調用控制器之前完成的,所以你的測試不調用此驗證。

還有另一種測試控制器的方法,你不直接調用控制器。而是構建並調用控制器映射的URL。這裏是如何做到這一點的好例子: http://rstoyanchev.github.com/spring-31-and-mvc-test/#1

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(loader=WebContextLoader.class, locations = {"classpath:/META-INF/spring/applicationContext.xml", "classpath:/META-INF/spring/applicationContext-test-override.xml", "file:src/main/webapp/WEB-INF/spring/webmvc-config.xml"}) 
public class MyControllerTest { 
@Autowired 
WebApplicationContext wac; 
MockMvc mockMvc; 

@Before 
public void setup() { 
    this.mockMvc = MockMvcBuilders.webApplicationContextSetup(this.wac).build(); 
} 

@Test 
@Transactional 
public void testMyController() throws Exception { 
    this.mockMvc.perform(get("/mycontroller/add?param=1").accept(MediaType.TEXT_HTML)) 
    .andExpect(status().isOk()) 
    .andExpect(model().attribute("date_format", "M/d/yy h:mm a")) 
    .andExpect(model().attribute("myvalue", notNullValue())) 
    .andExpect(model().attribute("myvalue", hasSize(2))) 
    .andDo(print()); 
} 
} 

POM(需使用彈簧里程碑式回購):

<!-- required for spring-test-mvc --> 
    <repository> 
     <id>spring-maven-milestone</id> 
     <name>Spring Maven Milestone Repository</name> 
     <url>http://maven.springframework.org/milestone</url> 
    </repository> 
... 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-test-mvc</artifactId> 
     <version>1.0.0.M1</version> 
     <scope>test</scope> 
    </dependency> 

注:彈簧MVC測試的lib是不是生產做好準備然而。實施中存在一些差距。我認爲它計劃在春季3.2完全實施。

這種方法是一個好主意,因爲它完全測試你的控制器。它很容易弄亂你的控制器映射,所以這些確實需要進行單元測試。

2

在調用控制器方法之前調用驗證程序 - 在將請求綁定到方法參數的過程中。由於在這種情況下您直接調用控制器方法,所以綁定和驗證步驟將被繞過。

讓它工作的方法是通過Spring MVC堆棧調用控制器 - 有幾種方法可以做到這一點,我覺得最好的方法是使用spring-test-mvc,它提供了一個很好的機制通過堆棧調用。

另一種方式通過堆棧調用是的HandlerAdapter注入到測試是這樣的:在測試

@Autowired 
private RequestMappingHandlerAdapter handlerAdapter; 

然後:

MockHttpServletRequest request = new MockHttpServletRequest("POST","/browser/create"); 
MockHttpServletResponse response = new MockHttpServletResponse(); 
httpRequest.addParameter(....);//whatever is required to create Browser.. 
ModelAndView modelAndView = handlerAdapter.handle(httpRequest, response, handler); 
2

基本上你實例化一個POJO與this.controller = new MyController(),則稱其方法爲this.controller.add(...)。只是簡單的Java對象,沒有任何上下文:@Valid不考慮。

@ContextConfiguration將只加載你可能的bean,可能的自定義驗證器等,但它不會做處理@Valid的魔法。

您需要的是模擬對控制器add方法的請求。完全模仿它,驗證包括在內。因爲你使用了一些Spring測試工具(來實例化一個MockHttpServletRequest),所以你離開這裏並不遙遠。

如果你使用Spring 3.0.x的或更少,你需要做的

new AnnotationMethodHandlerAdapter() 
     .handle(request, new MockHttpServletResponse(), this.controller); 

,使其工作。

如果您使用Spring 3.1+,上述解決方案將不起作用(see this link for more info)!您將需要使用this library(來自Spring團隊,所以不用擔心),同時等待他們將其集成到下一個Spring版本中。 然後,你將不得不做一些像

myMockController = MockMvcBuilders.standaloneSetup(new MyController()).build(); 
myMockController.perform(get("/browser/create")).andExpect(...); 

而且看看這些非常interesting slides從羅森Stoyanchev(我們這裏談的部分開始在幻燈片#116)!

注意:我不會進入關於這種測試是否被認爲是單元測試或集成測試的爭論。有人會說這是我們在這裏做的集成測試,因爲我們模擬了請求的完整路徑。但另一方面,你仍然可以用Mockito的類似@Mock註釋來模擬你的控制器(或者用其他任何模擬框架做類​​似的事情),所以有些人會說你可以將測試範圍縮小到幾乎純粹的「單元測試」 。當然你也可以單純地用簡單的舊Java +一個模擬框架來測試你的控制器,但在這種情況下,這將不允許你測試@Valid驗證。做出你的選擇 ! :)