3

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.

Clarkey
  • 1,553
  • 5
  • 22
  • 34
  • Do you insist on doing this only witj REST Assured? – Grzegorz Górkiewicz Feb 09 '17 at 21:53
  • @GrzegorzGórkiewicz yes, I do not want to introduce another testing framework. If I can't do it the way I want, then I'll deserialize the json into a List and iterate over it. – Clarkey Feb 09 '17 at 21:56
  • I didn't mean another **testing** framework. I wanted to use for example Google's GSON to instantiate a list of JSONObjects and then assert with Hamcrest that this list contains an element with the wanted property. – Grzegorz Górkiewicz Feb 09 '17 at 22:00

1 Answers1

18

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).

Johan
  • 37,479
  • 32
  • 149
  • 237
  • 1
    Thanks! This worked perfectly. This is a very elegant solution; much neater than having to deserialise the JSON and iterate. – Clarkey Feb 10 '17 at 13:33