Spring HATEOAS / MockMvc / JsonPath best practices
Asked Answered
T

3

6

I'm writing unit tests for Spring HATEOAS backend using MockMvc and JsonPath. To test the links contained in a response I'm doing something like:

@Test
public void testListEmpty() throws Exception {
    mockMvc.perform(get("/rest/customers"))
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(jsonPath("$.links", hasSize(1))) // make sure links only contains self link
            .andExpect(jsonPath("$.links[?(@.rel=='self')]", hasSize(1))) //  make sure the self link exists 1 time
            .andExpect(jsonPath("$.links[?(@.rel=='self')].href", contains("http://localhost/rest/customers{?page,size,sort}"))) // test self link is correct
            .andExpect(jsonPath("$.links[?(@.rel=='self')][0].href", is("http://localhost/rest/customers{?page,size,sort}"))) // alternative to test self link is correct
            .andExpect(jsonPath("$.content", hasSize(0))); // make sure no content elements exists
}

However I wonder if there are some best practices I should use to make it easier for myself like:

  • Testing link contains http://localhost does not feel right. Can I use some Spring MovkMvc helper to determine the host?
  • With JsonPath it's difficult to test if an array contains an element where 2 attributes have certain value. Like that the array should contain a self link with a certain value. Is there a better way to test that then above This will also come into play when testing validation errors for fields with error messages.

I've see technique like below in some blog posts:

.andExpect(jsonPath("$.fieldErrors[*].path", containsInAnyOrder("title", "description")))
.andExpect(jsonPath("$.fieldErrors[*].message", containsInAnyOrder(
    "The maximum length of the description is 500 characters.",
    "The maximum length of the title is 100 characters.")));

But this does not guarantee at all that title has the specific error message. It could also be that the title has incorrectly the "The maximum length of the description is 500 characters." but the test will succeed.

Taxiway answered 29/8, 2014 at 14:46 Comment(0)
S
2

You may use Traverson (included in Spring HATEOAS) to traverse the links in tests.

If you are using Spring Boot, I'd consider using @WebIntegrationTest("server.port=0") rather than MockMvc, as in some cases I experienced behaviours slightly different from the actual application.

You may find some example in a post of mine: Implementing HAL hypermedia REST API using Spring HATEOAS. Also look at the tests in the sample project.

Skyros answered 20/5, 2016 at 18:7 Comment(0)
C
0

One approach that addresses the http://localhost concern without sacrificing the need to test two attribute constraints on an array element is to use the org.hamcrest.CoreMatchers.hasItem(org.hamcrest.Matcher nestedMatcher) matcher. The test you showed above now becomes:

.andExpect(jsonPath("$.links[?(@.rel=='self')].href", hasItem(endsWith("/rest/customers{?page,size,sort}"))))
.andExpect(jsonPath("$.links[?(@.rel=='self')][0].href", hasItem(endsWith("/rest/customers{?page,size,sort}"))))
Ctesiphon answered 31/5, 2019 at 23:40 Comment(0)
S
0

You should use the links instead of making assertions on their href values.

https://github.com/Cosium/hal-mock-mvc allows easy Spring HATEOAS endpoint testing dedicated to HAL and HAL-FORMS. It is built on MockMvc.

Stem answered 21/5, 2023 at 22:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.