在 Spring Boot 1.41 中结合 @WebMcvTest 和 @PathVariable 注释控制器
Combine @WebMcvTest with @PathVariable annotated controllers in Spring Boot 1.41
此问题的示例应用程序在这里:https://github.com/olemerdy-fa/webmvctest
我正在 bootstrapping 一个使用 Spring Boot 1.4.1 的新项目。我尝试利用这个伟大框架的新功能,尤其是对我的应用程序进行(相当)单元测试的能力 'slices'。
当我在 @Controller
上声明 @PathVariable
注释方法时,我现在对 @WebMvcTest
功能感到困惑。
事实上,@WebMvcTest
应该 bootstrap 一个控制器和 MockMvc 测试工具,而不提供任何其他东西。使用 @MockBean
,提供模拟作为依赖项注入到此控制器中仍然很容易。
但是 @PathVariable
带注释的参数,其类型是,比如说,其转换器通常由 Spring Data 注册的 JPA 实体呢?
加入这个问题的示例项目包含几个示例:
MyEntity
是一个简单的 JPA 实体,MyEntityRepository
它的 Spring 数据关联存储库
Webmvctest1Controller
有一个 load
方法从路径中检索 id 并调用自己 MyEntityRepository.findOne(id)
方法
Webmvctest1ControllerUnitTest
通过模拟 MyEntityRepository
测试这个控制器,一切顺利
Webmvctest2Controller
有一个 load
方法,带有 @PathVariable
注释 MyEntity
由 Spring 数据注册转换器[查找=38=]
@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.Whosebug.MyEntity]; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.Whosebug.MyEntity]: no matching editors or conversion strategy found
MockHttpServletRequest:
HTTP Method = GET
Request URI = /load2/123
Parameters = {}
Headers = {Accept=[application/json]}
Handler:
Type = com.Whosebug.Webmvctest2Controller
Method = public com.Whosebug.MyEntity com.Whosebug.Webmvctest2Controller.load2(com.Whosebug.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.Whosebug.Webmvctest2ControllerUnitTest
load2(com.Whosebug.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.match(StatusResultMatchers.java:664)
at org.springframework.test.web.servlet.MockMvc.andExpect(MockMvc.java:171)
at com.Whosebug.Webmvctest2ControllerUnitTest.load2(Webmvctest2ControllerUnitTest.java:28)
WebmvctestApplicationTests
表明当应用程序完全 bootstrapped 时,在这两种情况下一切都很好
知道如何保留我的 @PathVariable
实体参数,同时仍然只用 @WebMvcTest
测试我的网络切片吗?
谢谢:)
感谢@zeroflagL 暗示对 HandlerMethodArgumentResolver
的可能贡献,我想出了一个似乎符合我需要的解决方案。贡献一个自定义转换器来处理控制器中的模拟实体注入似乎可以完成这项工作
@RunWith(SpringRunner.class)
@WebMvcTest(Webmvctest2Controller.class)
public class Webmvctest2ControllerUnitTest {
@Autowired
private MockMvc mvc;
@Test
public void load2() throws Exception {
mvc.perform(get("/load2/123").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{id:123,name:'My Entity'}"));
}
@TestConfiguration
static class InternalConfig {
@Bean
WebMvcConfigurer configurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(String.class, MyEntity.class, id -> {
if (id.equals("123")) {
MyEntity myEntity = new MyEntity(123);
myEntity.setName("My Entity");
return myEntity;
}
throw new IllegalArgumentException();
});
}
};
}
}
}
它并不完美,因为测试方法本身不能提供模拟实体,但它仍然允许我想保留的 web-unit-test bootstrap 和 运行。
此问题的示例应用程序在这里:https://github.com/olemerdy-fa/webmvctest
我正在 bootstrapping 一个使用 Spring Boot 1.4.1 的新项目。我尝试利用这个伟大框架的新功能,尤其是对我的应用程序进行(相当)单元测试的能力 'slices'。
当我在 @Controller
上声明 @PathVariable
注释方法时,我现在对 @WebMvcTest
功能感到困惑。
事实上,@WebMvcTest
应该 bootstrap 一个控制器和 MockMvc 测试工具,而不提供任何其他东西。使用 @MockBean
,提供模拟作为依赖项注入到此控制器中仍然很容易。
但是 @PathVariable
带注释的参数,其类型是,比如说,其转换器通常由 Spring Data 注册的 JPA 实体呢?
加入这个问题的示例项目包含几个示例:
MyEntity
是一个简单的 JPA 实体,MyEntityRepository
它的 Spring 数据关联存储库Webmvctest1Controller
有一个load
方法从路径中检索 id 并调用自己MyEntityRepository.findOne(id)
方法Webmvctest1ControllerUnitTest
通过模拟MyEntityRepository
测试这个控制器,一切顺利Webmvctest2Controller
有一个load
方法,带有@PathVariable
注释MyEntity
由 Spring 数据注册转换器[查找=38=]@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.Whosebug.MyEntity]; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.Whosebug.MyEntity]: no matching editors or conversion strategy found
MockHttpServletRequest:
HTTP Method = GET
Request URI = /load2/123
Parameters = {}
Headers = {Accept=[application/json]}
Handler:
Type = com.Whosebug.Webmvctest2Controller
Method = public com.Whosebug.MyEntity com.Whosebug.Webmvctest2Controller.load2(com.Whosebug.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.Whosebug.Webmvctest2ControllerUnitTest
load2(com.Whosebug.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.match(StatusResultMatchers.java:664)
at org.springframework.test.web.servlet.MockMvc.andExpect(MockMvc.java:171)
at com.Whosebug.Webmvctest2ControllerUnitTest.load2(Webmvctest2ControllerUnitTest.java:28)
WebmvctestApplicationTests
表明当应用程序完全 bootstrapped 时,在这两种情况下一切都很好
知道如何保留我的 @PathVariable
实体参数,同时仍然只用 @WebMvcTest
测试我的网络切片吗?
谢谢:)
感谢@zeroflagL 暗示对 HandlerMethodArgumentResolver
的可能贡献,我想出了一个似乎符合我需要的解决方案。贡献一个自定义转换器来处理控制器中的模拟实体注入似乎可以完成这项工作
@RunWith(SpringRunner.class)
@WebMvcTest(Webmvctest2Controller.class)
public class Webmvctest2ControllerUnitTest {
@Autowired
private MockMvc mvc;
@Test
public void load2() throws Exception {
mvc.perform(get("/load2/123").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{id:123,name:'My Entity'}"));
}
@TestConfiguration
static class InternalConfig {
@Bean
WebMvcConfigurer configurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(String.class, MyEntity.class, id -> {
if (id.equals("123")) {
MyEntity myEntity = new MyEntity(123);
myEntity.setName("My Entity");
return myEntity;
}
throw new IllegalArgumentException();
});
}
};
}
}
}
它并不完美,因为测试方法本身不能提供模拟实体,但它仍然允许我想保留的 web-unit-test bootstrap 和 运行。