Spring REST 控制器的单元测试 'Location' header
Unit Testing 'Location' header of Spring REST Controller
在 Spring REST Controller 中创建资源后,我将返回它在 header 中的位置,如下所示。
@RequestMapping(..., method = RequestMethod.POST)
public ResponseEntity<Void> createResource(..., UriComponentsBuilder ucb) {
...
URI locationUri = ucb.path("/the/resources/")
.path(someId)
.build()
.toUri();
return ResponseEntity.created(locationUri).build();
}
在单元测试中,我检查它的位置如下。
@Test
public void testCreateResource(...) {
...
MockHttpServletRequestBuilder request = post("...")
.content(...)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON);
request.session(sessionMocked);
mvc.perform(request)
.andExpect(status().isCreated())
.andExpect(header().string("Location", "/the/resources" + id);
}
此结果案例失败并显示以下消息。
java.lang.AssertionError: Response header Location expected:</the/resources/123456> but was:<http://localhost/the/resources/123456>
看来我必须为位置 header 提供上下文前缀 http://localhost
。
- 硬编码上下文安全吗?如果是,为什么?
- 如果不是,为测试用例正确生成它的正确方法是什么?
我猜是因为您正在使用 UriComponentsBuilder
构建您的 URI,它在您的位置 header 中设置了主机名。如果您使用 new URI("/the/resources")
之类的东西,您的测试就会通过。
在你的情况下,我会使用 redirectedUrlPattern 来匹配重定向 URL:
.andExpect(redirectedUrlPattern("http://*/the/resources"))
这将匹配任何主机名,因此您不必硬编码 localhost。详细了解可用于 AntPathMatcher
here.
的不同模式
如果您不需要在响应的位置 header 中有完整的 URI(即没有要求、设计约束等...):考虑切换为使用相对 URI(这是从 HTTP 标准的角度来看是有效的 - 请参阅 [1]: https://www.rfc-editor.org/rfc/rfc7231 )相对 URI 是现代浏览器和库支持的提议标准。这将允许您测试端点的行为并使其在长 运行.
中不那么脆弱。
如果你需要断言完整路径,因为你使用的是 MockMvc,你可以将测试请求中的 uri 设置为你想要的:
@Autowired
private WebApplicationContext webApplicationContext;
@Test
public void testCreateResource() {
MockMvc mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
mvc.perform(MockMvcRequestBuilders.get(new URI("http://testserver/the/resources")));
这将使注入的构建器在调用构建时生成“http://testserver”。请注意,如果未来的框架更改删除了此测试行为,可能会让您头疼。
在 Spring REST Controller 中创建资源后,我将返回它在 header 中的位置,如下所示。
@RequestMapping(..., method = RequestMethod.POST)
public ResponseEntity<Void> createResource(..., UriComponentsBuilder ucb) {
...
URI locationUri = ucb.path("/the/resources/")
.path(someId)
.build()
.toUri();
return ResponseEntity.created(locationUri).build();
}
在单元测试中,我检查它的位置如下。
@Test
public void testCreateResource(...) {
...
MockHttpServletRequestBuilder request = post("...")
.content(...)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON);
request.session(sessionMocked);
mvc.perform(request)
.andExpect(status().isCreated())
.andExpect(header().string("Location", "/the/resources" + id);
}
此结果案例失败并显示以下消息。
java.lang.AssertionError: Response header Location expected:</the/resources/123456> but was:<http://localhost/the/resources/123456>
看来我必须为位置 header 提供上下文前缀 http://localhost
。
- 硬编码上下文安全吗?如果是,为什么?
- 如果不是,为测试用例正确生成它的正确方法是什么?
我猜是因为您正在使用 UriComponentsBuilder
构建您的 URI,它在您的位置 header 中设置了主机名。如果您使用 new URI("/the/resources")
之类的东西,您的测试就会通过。
在你的情况下,我会使用 redirectedUrlPattern 来匹配重定向 URL:
.andExpect(redirectedUrlPattern("http://*/the/resources"))
这将匹配任何主机名,因此您不必硬编码 localhost。详细了解可用于 AntPathMatcher
here.
如果您不需要在响应的位置 header 中有完整的 URI(即没有要求、设计约束等...):考虑切换为使用相对 URI(这是从 HTTP 标准的角度来看是有效的 - 请参阅 [1]: https://www.rfc-editor.org/rfc/rfc7231 )相对 URI 是现代浏览器和库支持的提议标准。这将允许您测试端点的行为并使其在长 运行.
中不那么脆弱。如果你需要断言完整路径,因为你使用的是 MockMvc,你可以将测试请求中的 uri 设置为你想要的:
@Autowired
private WebApplicationContext webApplicationContext;
@Test
public void testCreateResource() {
MockMvc mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
mvc.perform(MockMvcRequestBuilders.get(new URI("http://testserver/the/resources")));
这将使注入的构建器在调用构建时生成“http://testserver”。请注意,如果未来的框架更改删除了此测试行为,可能会让您头疼。