Search code examples
springresttestngmockmvc

Unit Testing 'Location' header of Spring REST Controller


After creating a resource in Spring REST Controller , I am returning it's location in header as below.

@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();
}

In Unit Test, I am checking its location as below.

@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);
}

This result cases fails with following message.

java.lang.AssertionError: Response header Location expected:</the/resources/123456> but was:<http://localhost/the/resources/123456>

Seems like I have to provide context prefix http://localhost for Location header in expectation.

  • Is it safe to hard code context? If so, why?
  • If not, what is right way to generate it correctly for test case?

Solution

  • If you don't need to have a full URI in the Location header on the response (i.e. no requirement, design constraint etc...): Consider switching to use a relative URI ( which is valid from HTTP standards perspective - see [1]: https://www.rfc-editor.org/rfc/rfc7231 ) Relative URIs is a proposed standard that is supported by modern browsers and libraries. This will allow you to test the behavior of the endpoint and make it less fragile in the long run.

    If you need to assert the full path, since you are using MockMvc, you can set the uri in the test request to exactly what you want:

    @Autowired
    private WebApplicationContext webApplicationContext;
    
    @Test
    public void testCreateResource() {
        MockMvc mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        mvc.perform(MockMvcRequestBuilders.get(new URI("http://testserver/the/resources")));
    

    This will make the injected builder produce "http://testserver" when build is called. Note of caution, a framework change in the future could cause you headaches if they remove this test behavior.