REST Assured: How to find element using nested property in JSON and validate other properties
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).
Related videos on Youtube
Clarkey
Updated on June 04, 2022Comments
-
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 validatestatus.price
,status.source
, andstatus.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
, andstatus.price
of the element withtype.name
LPM could be incorrect, but this would not be detected as they would be matched against the element withtype.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 forstatus.id
,status.source
, andstatus.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.