REST Assured: How to find element using nested property in JSON and validate other properties

12,397

Here's one way to do it:

...
then().
        root("find {it.type.name == '%s'}.status").
        body("id", withArgs("LPM"), is(1)).
        body("price", withArgs("LPM"), is(1.20f)).
        body("source", withArgs("LPM"), is(172)).
        body("id", withArgs("ST"), is(10));

(you can obviously extract withArgs to a variable as well to avoid repetition).

find is Groovy's way of finding the first element matching the predicate ({it.type.name == '%s'}), findAll will always return a list.

root instructs REST Assured to use a "root path" that is used in subsequent expectations (see docs).

Share:
12,397

Related videos on Youtube

Clarkey
Author by

Clarkey

Updated on June 04, 2022

Comments

  • Clarkey
    Clarkey almost 2 years

    I have limited experience with rest assured. We have a number of tests that I can usually find examples within, or failing that google, but I am stuck when trying to match on a nested property for an element in an anonymous array, and verify properties up across and down (cousins?).

    Example JSON:

    [  
       {  
          "id":1,
          "type":{  
             "typeId":3,
             "name":"LPM"
          },
          "status":{  
             "id":1,
             "price":1.20,
             "source":172,
             "valid":0
          }
       },
       {  
          "id":2,
          "type":{  
             "typeId":2,
             "name":"ST"
          },
          "status":{  
             "id":10,
             "price":1.20,
             "source":172,
             "valid":0
          }
       }
    ]
    

    I'm using rest assured, and I want to find the element in the list that has type.name equal to LPM, and then validate status.price, status.source, and status.id of only that element.

    When I initially started validating the responses, it was only possible for one item to be in the array, so I was using:

    response.then()
            .assertThat()
            .body("size", greaterThan(0))
            .body("[0].type.name", equalToIgnoringCase("LPM"))
            .body("[0].status.id", equalTo(statusId))
            .body("[0].status.source", equalTo(sourceId))
            .body("[0].status.price", equalTo(price));
    

    However this is no longer assured to work, as there may be more than one element in the array, and the order is not guaranteed.

    I have modified my body checks to be:

    response.then()
            .assertThat()
            .body("size", greaterThan(0))
            .body("type.name", hasItem("LPM"))
            .body("status.id", hasItem(statusId))
            .body("status.source", hasItem(sourceId))
            .body("status.price", hasItem(price));
    

    This is enough to get the test to pass, but this introduces the risk that the status.id, status.source, and status.price of the element with type.name LPM could be incorrect, but this would not be detected as they would be matched against the element with type.name ST.

    So I want to be able to find the element which has LPM as it's type.name, of which I can guarantee there to be only one, and then for status.id, status.source, and status.price, check that element only, i.e. NOT the ST element.

    I tried to modify my body matcher to find the elements with the type.name that I need, but I can't get this to work, as I can't figure out how to go back up the tree, across and down, to check other attributes in the same element:

    response.then()
            .assertThat()
            .body("size", greaterThan(0))
            .body("$.findAll (it.type.name = LPM}.status.id ", hasItem(statusId))
            .body("$.findAll (it.type.name = LPM}.status.source", hasItem(sourceId))
            .body("$.findAll (it.type.name = LPM}.status.price", hasItem(price));
    

    Also even if this worked, it would search the tree 3 times when really once would have done.

    My middle matches have this test passing for now but I want to get this right. I'm aware I could get the elements into a List and work it out from there, but for consistency with the rest of our examples I'd rather not, though I currently can't see another option.

    I have tried to find examples of what I'm trying to do in documentation, multiple rest assured tutorials and examples but I haven't, so there is always the possibility that this isn't actually possible. If not I'm happy to educate don the theory behind it.

    Any help appreciated.