Spring REST控制器的单元测试“位置”标题

6bc51xsx  于 2023-01-16  发布在  Spring
关注(0)|答案(3)|浏览(161)

在SpringRESTController中创建一个资源后,我将返回它在头中的位置,如下所示。

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

看起来我必须为位置标题提供上下文前缀http://localhost

  • 硬编码上下文安全吗?如果安全,为什么?
  • 如果没有,正确生成测试用例的正确方法是什么?
9udxz4iz

9udxz4iz1#

我猜是因为你使用UriComponentsBuilder来构建你的URI,所以它在你的位置头中设置了主机名,如果你只使用new URI("/the/resources")这样的东西,你的测试就会通过。
在您的情况下,我会使用redirectedUrlPattern来匹配重定向URL:
.andExpect(redirectedUrlPattern("http://*/the/resources"))
这将匹配任何主机名,因此您不必硬编码 localhost。了解更多关于可用于AntPathMatcherhere的不同模式的信息。

ylamdve6

ylamdve62#

如果您不需要在响应的Location标头中包含完整的URI(即无要求、设计约束等):考虑切换到使用相对URI(从HTTP标准的Angular 来看是有效的-参见[1]:https://www.rfc-editor.org/rfc/rfc7231)Relative URI是一个被提议的标准,它被现代浏览器和库所支持,这将允许您测试端点的行为,并使其在长期运行中不那么脆弱。
如果您需要Assert完整路径,因为您使用的是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”。注意,如果将来的框架更改删除了此测试行为,可能会让您头疼。

igsr9ssn

igsr9ssn3#

面对同样的问题,我尝试了 Suraj Bajaj 提供的解决方案,对我来说效果很好。
然后我尝试直接Assert头字段“Location”的文本,最后使用了containsString()
这个简单的解决方案也应该是一个可行的替代方案,只要服务器上下文和相对/绝对路径的问题不重要:

mvc.perform(request)
  .andExpect(status().isCreated())
  .andExpect(header().string("Location", containsString("/the/resources"+id)));

相关问题