【问题标题】:How do I get Spring MVC to invoke validation in a JUnit test?如何让 Spring MVC 在 JUnit 测试中调用验证?
【发布时间】:2012-08-31 18:04:54
【问题描述】:

我有一个名为 Browser 的 POJO,我使用 Hibernate Validator 注释进行了注释。

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

这是我的 Controller 的 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 被识别(和验证)?

谢谢,

马特

【问题讨论】:

    标签: java spring spring-mvc junit bean-validation


    【解决方案1】:

    基本上你用this.controller = new MyController() 实例化了一个POJO,然后调用它的方法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(...);
    

    还可以看看来自 Rossen Stoyanchev 的 interesting slides(我们在这里讨论的部分从幻灯片 #116 开始)!

    注意:我不会讨论这种测试是否被视为单元测试或集成测试。有人会说这是我们在这里进行的集成测试,因为我们模拟了请求的完整路径。但另一方面,您仍然可以使用来自 Mockito 的 @Mock 注释来模拟您的控制器(或使用任何其他模拟框架做类​​似的事情),所以其他人会说您可以将测试范围缩小到几乎纯粹的“单元测试” .当然,您也可以使用普通的旧 Java + 模拟框架对您的控制器进行纯粹的单元测试,但在这种情况下,这将不允许您测试 @Valid 验证。做出你的选择 ! :)

    【讨论】:

      【解决方案2】:

      验证是在调用控制器之前完成的,因此您的测试不会调用此验证。

      还有另一种测试控制器的方法,您不直接调用控制器。相反,您构建并调用映射控制器的 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(需要使用 spring 里程碑 repo):

          <!-- 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>
      

      注意:spring-mvc-test 库尚未准备好生产。实施中存在一些差距。我认为它计划在春季 3.2 全面实施。

      这种方法是一个好主意,因为它可以全面测试您的控制器。很容易弄乱你的控制器映射,所以这些确实需要进行单元测试。

      【讨论】:

        【解决方案3】:

        在调用控制器方法之前调用验证器 - 在将请求绑定到方法参数的过程中。由于在这种情况下您直接调用控制器方法,因此绕过了绑定和验证步骤。

        让它工作的方法是通过 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);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-08-07
          • 2013-04-16
          • 2013-09-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多