BAD_SEED
BAD_SEED

Reputation: 5056

Full Json match with RestAssured

I'm using REST-Assured to test some API. My API clearly respond with a JSON and according to the doc if this is the response:

{
    "id": "390",
    "data": {
        "leagueId": 35,
        "homeTeam": "Norway",
        "visitingTeam": "England",
    },
    "odds": [{
        "price": "1.30",
        "name": "1"
    },
    {
        "price": "5.25",
        "name": "X"
    }]
}

I could test like this:

@Test
public void givenUrl_whenSuccessOnGetsResponseAndJsonHasRequiredKV_thenCorrect() {
   get("/events?id=390")
      .then()
         .statusCode(200)
         .assertThat()
            .body("data.leagueId", equalTo(35)); 
}

Surely this is readable but I would a full comparison of the JSON (i.e.: this is the JSON response; this is a canned JSON - a resource file would be perfect - are those JSON equals?). Does REST-Assured offer something like that or I need to make it manually.

Upvotes: 28

Views: 31582

Answers (8)

Ashwiinn Karaangutkar
Ashwiinn Karaangutkar

Reputation: 171

You can make use of JSONAssert Library to match the entire JSON Response. I recently wrote a blog on how to achieve it.

Below is the basic usage on how to use the library:

    JSONAssert.assertEquals(expectedResponse, actualResponse, JSONCompareMode.LENIENT);

Upvotes: 0

You can use Validate with JSON SCHEMA in RestAssured.

Try this code:

// Base Test [BaseTest.java]

public class BaseTest {

    protected RequestSpecification requestSpecificationToMerge = new RequestSpecBuilder()
            .setBaseUri("BASE URL")
            .setContentType(ContentType.JSON)
            .build();

    @BeforeMethod
    public void setFilter() {
        RestAssured.filters(new AllureRestAssured());
    }

}

// Test [Home.java]

public class Home extends BaseTest {
    
    @Test(priority = 0)
    public void getHome() {
        
        given()
        .spec(requestSpecificationToMerge)
        .basePath("/your endpoint")
        .when()
        .get()
        .then()
        .log().body()
        .body(matchesJsonSchemaInClasspath("home.json"));
    }

// JSON SCHEMA [home.json]

{
    "type": "object",
    "required": [
        "data",
        "meta",
        "status"
    ],
    "properties": {
        "data": {
            "type": ["array", "null"],
            "items": {
                "type": "object",
                "required": [
                    "id",
                    "title",
                    "sorting"
                ],
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "title": {
                        "type": "string"
                    },
                    "sorting": {
                        "type": "integer"
                    }
                }
            }
        },
        "meta": {
            "type": ["object", "null"],
            "required": [
                "pagination"
            ],
            "items": {
                "type": "object",
                "required": [
                    "current_page",
                    "per_page",
                    "total",
                    "total_page"
                ],
                "properties": {
                    "current_page": {
                        "type": "integer"
                    },
                    "per_page": {
                        "type": "integer"
                    },
                    "total": {
                        "type": "integer"
                    },
                    "total_page": {
                        "type": "integer"
                    }
                }
            }
        },
        "status": {
            "type": "object",
            "required": [
                "code",
                "message_client",
                "message_server"
            ],
            "properties": {
                "code": {
                    "type": "integer",
                    "enum": [
                        200,
                        404
                    ]
                },
                "message_client": {
                    "type": "string"
                },
                "message_server": {
                    "type": "string"
                }
            }
        }
    }
}

Upvotes: 1

Sergei Voitovich
Sergei Voitovich

Reputation: 2952

Apparently, rest-assured only provides capabilities to validate the schema as described here.

However, it's quite simple to make an exact comparison using jackson-databind and junit.

We should write a function that compares a body returned by rest-assured with a file in the resources directory

import org.junit.Assert;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

void assertJsonEquals(String expectedJson, ResponseBodyExtractionOptions actualBody) throws IOException {
    Assert.assertNotNull("The request returned no body", expectedJson);

    final ObjectMapper mapper = new ObjectMapper();
    Assert.assertEquals(
            mapper.readTree(Objects.requireNonNull(getClass().getClassLoader().getResource(expectedJsonPath)).openStream().readAllBytes()),
            mapper.readTree(body.asByteArray())
    );
}

Then, use it as shown below

final ResponseBodyExtractionOptions actualBody = given()
        .accept("application/json")
        .contentType(MediaType.APPLICATION_JSON)
    .when()
        .get("...")
    .then()
        .extract().body();
assertJsonEquals("expected-payload.json", actualBody);

Upvotes: 0

Anton Koshevyi
Anton Koshevyi

Reputation: 21

If somebody is looking for method without parsing json-file.

You can check the body size at the beginning using Matchers.aMapWithSize(size), and then check the contents as usual.

Example:

@Test
public void getAccount_forbidden_whenUserIsAnonymous() {
    RestAssured
        .get("/account")
        .then()
        .statusCode(HttpServletResponse.SC_FORBIDDEN)
        .body("", Matchers.aMapWithSize(5),
              "timestamp", Matchers.notNullValue(),
              "status", Matchers.equalTo(HttpServletResponse.SC_FORBIDDEN),
              "error", Matchers.equalTo("Forbidden"),
              "message", Matchers.equalTo("Access Denied"),
              "path", Matchers.equalTo("/account"));
}

Upvotes: 1

HaC
HaC

Reputation: 950

Use RestAssured's JsonPath to parse the json file into a Map and then compare it with Hamcrest Matchers. This way the order etc didn't matter.

import static org.hamcrest.Matchers.equalTo;
import io.restassured.path.json.JsonPath;

...

JsonPath expectedJson = new JsonPath(new File("/path/to/expected.json"));

given()
    ...
    .then()
    .body("", equalTo(expectedJson.getMap("")));

Upvotes: 18

BigGinDaHouse
BigGinDaHouse

Reputation: 1346

REST Assured does not support JSON comparison, only schema and parts of the body as you have in your question. What you can do is using Hamcrest's JSON comparitorSameJSONAs in Hamcrest JSON SameJSONAs

Upvotes: 3

Peter Thomas
Peter Thomas

Reputation: 58058

Karate is exactly what you are looking for - you can perform a full equality match of a JSON payload in one step.

And for the cases where you have dynamic values (generated keys, timestamps) Karate provides a very elegant way for you to ignore (or just validate the format of) these keys.

One the primary motivations for creating Karate was to come up with a better alternative to REST-assured. You can refer to this document which can help you evaluate Karate and make a case for it in your organization: Karate vs REST-assured.

Upvotes: 7

RocketRaccoon
RocketRaccoon

Reputation: 2599

Easy way:

String s = "{\"ip\": \"your_ip\"}";
given().log().all().contentType(ContentType.JSON).get("http://ip.jsontest.com/").then().log().all().assertThat().body(containsString(s))

Not easy way: you can create custom matcher or use jsonpath, it has options to comapre jsons.

Upvotes: 0

Related Questions