0
votes

I have a controller that returns a list of sections. Each of these sections have a type. I want to test that the section of type "JOB" have a field "functions" with a size of 3.

Here is my code:

mockMvc.perform(get(url)
        .contentType(MediaTypes.HAL_JSON)
        .accept(MediaTypes.HAL_JSON))
        .andDo(print())
        .andExpect(status().isOk())
        .andExpect(jsonPath("$._embedded.content", 
                hasSize(equalTo(2))))
        .andExpect(jsonPath("$._embedded.content[?(@.type=='JOB')].functions", 
                hasSize(equalTo(3))));

The JSON returned by the controller (simplified for legibility):

{
   "_embedded":{
      "content":[
         {
            "subsections":null,
            "type":"PROFILE",
            "createdDate":null,
            "lastModifiedDate":null
         },
         {
            "functions":[
               {
                  "rank":845131,
                  "function":"9IXZT"
               },
               {
                  "rank":82701,
                  "function":"T91WX"
               },
               {
                  "rank":98686,
                  "function":"PA7NA"
               }
            ],
            "type":"JOB",
            "createdDate":null,
            "lastModifiedDate":null
         }
      ]
   }
}

I tested the expression with this tool against that JSON and works correctly, but when running the test I'm getting this assertion error:

java.lang.AssertionError: JSON path "$._embedded.content[?(@.type=='JOB')].functions"
Expected: a collection with size <3>
    but: collection size was <1>
        at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
        at org.springframework.test.util.JsonPathExpectationsHelper.assertValue(JsonPathExpectationsHelper.java:74)
        at org.springframework.test.web.servlet.result.JsonPathResultMatchers$1.match(JsonPathResultMatchers.java:86)
        at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171)
        ...
2

2 Answers

2
votes

There is a difference when adding the .*. When I fill in your data at JSONPath Expression Tester I get a slight but important difference:

Case 1

$._embedded.content[?(@.type=='JOB')].functions yields

[
   [
      {
         "rank":845131,
         "function":"9IXZT"
      },
      {
         "rank":82701,
         "function":"T91WX"
      },
      {
         "rank":98686,
         "function":"PA7NA"
      }
   ]
]

Case 2

and $._embedded.content[?(@.type=='JOB')].functions.* yields

[
   {
      "rank":845131,
      "function":"9IXZT"
   },
   {
      "rank":82701,
      "function":"T91WX"
   },
   {
      "rank":98686,
      "function":"PA7NA"
   }
]

So for case 1 when you take the root node (which is an array) you have one element as direct child: the array containing three objects. Therefore the assertion found an array (collection) whith size one. For case 2 you have the array root node containing three objects leading to a count of three for the collection.

Maybe this is not true for other implementations. In this example I used the JSONPath 0.3.4 Implementation under Flow Communications.


The . after functions sets the root level of the result to this element and * means take all elements under this root.

I am not confident enough to explain the exact behaviour behind $._embedded.content[?(@.type=='JOB')].functions. But I think it has something to do with the filter ?(@.type=='JOB')

https://github.com/json-path/JsonPath#what-is-returned-when

0
votes

Adding .* to the end of the expression solved the problem, so now it looks like this:

$._embedded.content[?(@.type=='JOB')].functions.*

I don't understand why the first expression in the example is working but I have to add .* to the second one. If someone knows, please explain.