2012-09-06 28 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验证。做出你的选择 ! :)