java 如何让 Spring MVC 在 JUnit 测试中调用验证?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12308213/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I get Spring MVC to invoke validation in a JUnit test?
提问by Matt Raible
I have a POJO called Browser that I've annotated with Hibernate Validator annotations.
我有一个名为 Browser 的 POJO,我用 Hibernate Validator 注释对其进行了注释。
import org.hibernate.validator.constraints.NotEmpty;
public class Browser {
@NotEmpty
private String userAgent;
@NotEmpty
private String browserName;
...
}
I've written the following unit test that tries to verify my Controller method catches validation errors.
我编写了以下单元测试,尝试验证我的 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());
}
Here's my Controller's add() method:
这是我的控制器的 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;
}
The problem I'm experiencing is that result never has errors, so it's like @Valid isn't getting recognized. I tried adding the following to my test class, but it doesn't solve the problem.
我遇到的问题是结果永远不会有错误,所以就像 @Valid 没有得到认可。我尝试将以下内容添加到我的测试类中,但并没有解决问题。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"file:path-to/WEB-INF/spring-mvc-servlet.xml"})
Does anyone know how I'd get @Valid to be recognized (and validated) when testing with JUnit?
有谁知道在使用 JUnit 进行测试时如何让 @Valid 被识别(和验证)?
Thanks,
谢谢,
Matt
马特
采纳答案by Solubris
The validation is done before the call to the controller, so your test is not invoking this validation.
验证在调用控制器之前完成,因此您的测试不会调用此验证。
There is another approach to testing controllers, where you dont invoke the controller directly. Instead you construct and call the URL that the controller is mapped on. Here is a good example of how to do this: http://rstoyanchev.github.com/spring-31-and-mvc-test/#1
还有另一种测试控制器的方法,您不直接调用控制器。相反,您构造并调用映射到控制器的 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 (need to use spring milestone repo):
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>
NOTE: the spring-mvc-test lib is not production ready yet. There are some gaps in the implementation. I think its planned to be fully implemented for spring 3.2.
注意: spring-mvc-test 库还没有准备好生产。执行上存在一些差距。我认为它计划在春季 3.2 全面实施。
This approach is a great idea as it tests your controllers fully. Its easy to mess up your controller mappings, so these do really need to be unit tested.
这种方法是一个好主意,因为它可以完全测试您的控制器。很容易弄乱你的控制器映射,所以这些确实需要进行单元测试。
回答by Biju Kunjummen
Validators are called ahead of the controller methods being invoked - during the process of binding the request to the method parameters. Since in this case you are invoking the controller method directly, the binding and the validation steps are being bypassed.
在调用控制器方法之前调用验证器 - 在将请求绑定到方法参数的过程中。由于在这种情况下您直接调用控制器方法,因此将绕过绑定和验证步骤。
The way to get it to work will be to make the call to the controller through the Spring MVC stack - There are a few ways to do this, I feel the best way is to use spring-test-mvcwhich provides a nice mechanism to call through the stack.
让它工作的方法是通过 Spring MVC 堆栈调用控制器 - 有几种方法可以做到这一点,我觉得最好的方法是使用spring-test-mvc,它提供了一个很好的机制通过堆栈调用。
Another way to call through the stack is to inject in HandlerAdapter to the test this way:
另一种通过堆栈调用的方法是通过这种方式将 HandlerAdapter 注入到测试中:
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;
Then in the test:
然后在测试中:
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);
回答by Jerome Dalbert
Basically you instantiated a POJO with this.controller = new MyController()
, then called its method this.controller.add(...)
. Just simple Java with a simple object, without any context : @Valid is not taken into account.
基本上,您使用 实例化了一个 POJO this.controller = new MyController()
,然后调用了它的方法this.controller.add(...)
。只是带有简单对象的简单 Java,没有任何上下文:不考虑 @Valid。
@ContextConfiguration will just load your possible beans, with possible custom validators and such, but it won't do the magic of processing @Valid.
@ContextConfiguration 只会加载您可能的 bean,使用可能的自定义验证器等,但它不会执行处理 @Valid 的魔力。
What you need is something to emulate a request to the controller's add
method. Completely emulate it, validation included.
You were not far from doing so, since you used some Spring testing facilities (to instantiate a MockHttpServletRequest).
您需要的是模拟对控制器add
方法的请求。完全模拟它,包括验证。您离这样做不远了,因为您使用了一些 Spring 测试工具(实例化 MockHttpServletRequest)。
If you use Spring 3.0.x or less, you need to do
如果您使用 Spring 3.0.x 或更低版本,则需要执行
new AnnotationMethodHandlerAdapter()
.handle(request, new MockHttpServletResponse(), this.controller);
to make it work.
使其工作。
If you use Spring 3.1+, the above solution won't work (see this link for more info) ! You will need to use this library(from Spring team, so it's sound don't worry), while waiting for them to integrate it in next Spring version. Then you will have to do something like
如果您使用 Spring 3.1+,上述解决方案将不起作用(有关更多信息,请参阅此链接)!您将需要使用这个库(来自 Spring 团队,所以听起来不用担心),同时等待他们将它集成到下一个 Spring 版本中。然后你将不得不做类似的事情
myMockController = MockMvcBuilders.standaloneSetup(new MyController()).build();
myMockController.perform(get("/browser/create")).andExpect(...);
Also have a look at these very interesting slidesfrom Rossen Stoyanchev (the part we are talking about here begins at slide #116) !
还可以看看Rossen Stoyanchev 的这些非常有趣的幻灯片(我们在这里谈论的部分从幻灯片 #116 开始)!
Note : I won't enter in the debate of whether or not this kind of testing is considered as unit testing or integration testing. Some would say this is rather integration testing we are doing here, since we emulate the full path of a request. But on another hand you can still mock your controller with like @Mock annotations from Mockito (or do similar stuff with any other mocking framework), so some others would say that you can reduce the scope of the test to almost pure "unit testing". Of course you can alternatively and purely unit test your controller with plain old Java + a mocking framework, but in this case this won't allow you to test the @Valid validation. Make your choice ! :)
注意:我不会参与这种测试是否被视为单元测试或集成测试的争论。有人会说这是我们在这里进行的集成测试,因为我们模拟了请求的完整路径。但另一方面,您仍然可以使用来自 Mockito 的 @Mock 注释来模拟您的控制器(或使用任何其他模拟框架做类似的事情),因此其他一些人会说您可以将测试范围缩小到几乎纯粹的“单元测试” . 当然,您也可以使用普通的旧 Java + 模拟框架对您的控制器进行纯粹的单元测试,但在这种情况下,这将不允许您测试 @Valid 验证。做出你的选择 !:)