【发布时间】:2017-03-07 12:53:18
【问题描述】:
此问题的示例应用程序在这里:https://github.com/olemerdy-fa/webmvctest
我正在使用 Spring Boot 1.4.1 引导一个新项目。我尝试利用这个出色框架的新功能,尤其是对我的应用程序“切片”进行(相当)单元测试的能力。
我现在在 @Controller 声明 @PathVariable 注释方法时使用 @WebMvcTest 功能。
确实,@WebMvcTest 应该引导单个控制器和 MockMvc 测试工具,而不提供任何其他内容。使用@MockBean,提供模拟作为依赖注入到这个控制器中仍然很容易。
但是 @PathVariable 注释参数的类型是,例如,其转换器通常由 Spring Data 注册的 JPA 实体呢?
加入此问题的示例项目包含一些示例:
-
MyEntity是一个简单的 JPA 实体,MyEntityRepository是它的 Spring Data 关联存储库 -
Webmvctest1Controller有一个load方法从路径中检索 id 并调用自己的MyEntityRepository.findOne(id)方法 -
Webmvctest1ControllerUnitTest通过模拟MyEntityRepository来测试这个控制器,一切顺利 -
Webmvctest2Controller有一个load方法,其中@PathVariable注释MyEntity由 Spring Data 注册转换器查找@RestController public class Webmvctest2Controller { @RequestMapping("load2/{id}") public MyEntity load2(@PathVariable("id") MyEntity myEntity) { return myEntity; } } -
Webmvctest2ControllerUnitTest是我卡住的地方,因为我不知道如何在仍然使用MockMvc的同时提供模拟实体作为参数@RunWith(SpringRunner.class) @WebMvcTest(Webmvctest2Controller.class) public class Webmvctest2ControllerUnitTest { @Autowired private MockMvc mvc; @Test public void load2() throws Exception { // How do I mock converter to PathVariable here? mvc.perform(get("/load2/123").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().json("{id:123,name:'My Entity 123'}")); } }
这会失败并出现org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException 异常
2016-10-25 14:27:55.699 WARN 20753 --- [ main] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to convert request element: org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException: Failed to convert value of type [java.lang.String] to required type [com.stackoverflow.MyEntity]; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.stackoverflow.MyEntity]: no matching editors or conversion strategy found
MockHttpServletRequest:
HTTP Method = GET
Request URI = /load2/123
Parameters = {}
Headers = {Accept=[application/json]}
Handler:
Type = com.stackoverflow.Webmvctest2Controller
Method = public com.stackoverflow.MyEntity com.stackoverflow.Webmvctest2Controller.load2(com.stackoverflow.MyEntity)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 500
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.375 sec <<< FAILURE! - in com.stackoverflow.Webmvctest2ControllerUnitTest
load2(com.stackoverflow.Webmvctest2ControllerUnitTest) Time elapsed: 0.015 sec <<< FAILURE!
java.lang.AssertionError: Status expected:<200> but was:<500>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:54)
at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:81)
at org.springframework.test.web.servlet.result.StatusResultMatchers$10.match(StatusResultMatchers.java:664)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171)
at com.stackoverflow.Webmvctest2ControllerUnitTest.load2(Webmvctest2ControllerUnitTest.java:28)
-
WebmvctestApplicationTests表明,当应用程序完全启动时,这两种情况下一切都很好
知道如何在保留@PathVariable 实体参数的同时仅使用@WebMvcTest 测试我的网络切片吗?
谢谢:)
【问题讨论】:
-
" @WebMvcTest 应该引导单个控制器和 MockMvc 测试工具,而不提供任何其他内容。" 你基本上回答了你自己的问题,所以我没有确定您期望的答案。你不需要
@WebMvcTest来使用MockMvc。更重要的是:请求没有正文,那么为什么该方法需要一个实体作为参数呢? -
这里给出的示例很简单地显示了正在发生的事情。在现实世界的代码中,该方法不会简单地转发实体,而是可以修改它、请求子属性、使用多种可转换的方法。实际上,如果在请求签名中加载实体是一个问题,那么对于转换器未在 Spring 本地注册的任何类型(例如:Joda Money),该问题仍然有效。
-
这里,我们只能在加载所有内容时测试我们的控制器。我正在寻找一种保持切片 (docs.spring.io/spring-boot/docs/current/reference/html/…) 精神的解决方案,它只加载我们正在测试的层的少数服务(这里只加载 Web 部件,而不是数据或服务层)。我们能做些什么来只加载 Web 控制器并模拟其所有依赖项而不修改原始控制器代码(我想保留 @PathVariable 丰富的类型并让 Spring 处理转换)?
-
对于未在 Spring 本地注册的转换器:"@WebMvcTest 将自动配置 Spring MVC 基础结构并将扫描的 bean 限制为... HandlerMethodArgumentResolver"。至于“我正在寻找一种保持切片精神的解决方案” - 再说一遍:你不需要
@WebMvcTest。你可以自己设置MockMvc。 -
关于 MockMvc 可以单独引导这一事实是绝对正确的。我想依靠@WebMvcTest 来限制扫描bean 的范围。非常感谢您指出 Spring 扫描 HandlerMethodArgumentResolver,我将尝试添加一个仅为此测试注册的自定义范围,看看它是否能很好地满足我们的需要:)
标签: spring-mvc spring-boot spring-test