Search code examples
restrest-assuredrest-assured-jsonpath

Rest Assured: Isn't there a better way to test an array of results?


I am studying Rest Assured framework now.

Using some API I get the following (partial) JSON response:

{
    "results": [
        {
            "type": "AAAA"
        },
        {
            "type": "A"
        }
    ]
}

I am trying to verify the types. The only way I found so far is to use gson to translate the string to an object and then assert:

@Given("^test2$")
public void test2$() {

    RestAssured.baseURI = BASE_URI;

    String response =
            given()
            .param(KEY_KEY, API_KEY)
            .param(URL_KEY, URL_TO_CHECK)
            .when()
            .get(RESOURCE)
            .asString();

    System.out.println(response);

    GsonBuilder builder = new GsonBuilder();
    builder.setPrettyPrinting();
    Gson gson = builder.create();
    WhtResponse whtResponse = gson.fromJson(response, WhtResponse.class);

    assertTrue(whtResponse.getResults().size() == 2);
    assertTrue(whtResponse.getResults().get(0).getType().equals("AAAA"));
    assertTrue(whtResponse.getResults().get(1).getType().equals("A"));
}

Please ignore that there are several asserts in one test method. I know it is not best practice but I am just "playing" now to study the material.

Is there a better, shorter, more fluent way to test both values? Maybe directly with Rest Assured and without the Gson?

Thanks!


Solution

  • There are probably a few ways but I want to present a specific one, using only RestAssured.

    First, of all, you can create Java classes to represent the given JSON. You can always expand it.

    Create a class ResultsObject (name doesn't matter)

    public class ResultsObject {
        public List<TypeObject> results;
    }
    

    We are declaring that RestAssured have to look for a JSON Array of JSON Objects. Each of these Object is parsed to TypeObject (this name also doesn't matter). What matters, is the results variable name. It matches the results in the JSON.

    So, now we have to create TypeObject class to represent each of JSON Objects.

    public class TypeObject {
        public String type;
    }
    

    Again, the class name doesn't matter, what matters is type variable name. Just like in JSON.

    Now, with a single line of code we can parse the JSON (either from Response or String) to the classes we created like this:

    ResultsObject results = response.jsonPath().get("$", ResultObject.class);

    Further work requires to create an assertion.

    MatcherAssert.assertThat(types, Matchers.hasItems("AAAA", "A")); //you can use static imports if you'd like.
    

    The assertion is from Hamcrest which is already included in RestAssured. It matches the Array of Strings to an Array of Strings of our choice.

    You can notice, that types is not yet initialized. We need an Array of Strings but it's an Array of TypeObject. We can simply convert it using Java Stream API like this:

    List<String> types = resultsObject.results.stream().map(x -> x.type).collect(Collectors.toList());
    

    And that's it!

    But the way, if you get the Exception like this Cannot deserialize object because no JSON deserializer found in classpath. Please put either Jackson (Databind) or Gson in the classpath. all you have to do is add jackson-databind to Maven. Rest Assured is able to use Gson or Jackson databind to transform JSON into Java classes. All you need is the dependency, and you are all set.

            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.9.9</version>
            </dependency>
    

    I already tested the solution. It also gives you the flexibility of how you want to process the data, and as you can see, I used only 1 Assertion to compare all of the types from JSON.

    Hope it helps!