Search code examples
javajsonpathhamcrestmockmvcspring-mvc-test

Matcher for JSONPath expression to find order of fields


I have an API that returns a JSON object that will be displayed in a UI. The order of the fields in the object matters because the UI wants to display the fields in that order. I want to write a unit test to ensure the order of the fields in the object is correct. My API response looks like this:

{
    "data": {
        "field1": "value1",
        "field2": "value2"
     }
}

Using spring boot's MockMvc library, I want to write an expression like this to validate that the fields are coming back in order:

.andExpect(jsonPath("$.data"), is("field1=value1, field2=value2")

Obviously, the above doesn't work but I'm not sure what type of Matcher I should be using to try and validate that the fields are in order.


Solution

  • I couldn't find a Hamcrest matcher that compares the order of the fields, but it is easy enough to write your own. Note that the result of the JSON path expression will give you a LinkedHashMap so all you need is a Matcher that takes in a LinkedHashMap and compares it to the given object, entry by entry in order. Converting the map to a List of entries and using the equals method of List does the trick since List will compare items in order.

    Here's how the matcher looks:

    private class OrderedMapMatcher extends BaseMatcher<Map<String, String>> {
    
        private LinkedHashMap<String, String> expected;
    
        public OrderedMapMatcher(LinkedHashMap<String, String> expected) {
            this.expected = expected;
        }
    
        @Override
        public boolean matches(Object o) {
            if (!(o instanceof LinkedHashMap)) {
                return false;
            }
            LinkedHashMap actual = (LinkedHashMap) o;
            List<Map.Entry<String, String>> expectedList = new ArrayList<>(expected.entrySet());
            List actualList = new ArrayList(actual.entrySet());
            return expectedList.equals(actualList);
        }
    
        @Override
        public void describeTo(Description description) {
            description.appendText(expected.toString());
        }
    }
    

    And here's how the assertion looks:

    LinkedHashMap<String, String> expectedData = new LinkedHashMap<>();
    expectedData.put("field1", "value1");
    expectedData.put("field2", "value2");
    
    // mockMvc ResultActions code would be here
    .andExpect(jsonPath("$.data", new OrderedMapMatcher(expectedData)));