Introduction

In our REST Assured series, we’ve covered topics like Status Code Validation, Response Specification, and PATCH Requests, building a strong foundation for API testing. Now, we’ll explore JSON Path, a powerful feature in REST Assured for extracting and validating data from JSON responses. This guide demonstrates how to use JSON Path to navigate and test JSON structures, ensuring accurate API behavior. It’s designed for beginners and experienced developers, offering clear explanations and practical examples.

Key Point: JSON Path in REST Assured enables you to query and validate specific elements in JSON responses, simplifying complex data extraction and verification in API tests.

What is JSON Path?

JSON Path is a query language for JSON, similar to XPath for XML, allowing you to select and extract data from JSON structures using a concise syntax path. In REST Assured, JSON Path is integrated to parse JSON responses, enabling you to:

  • Extract specific fields or nested values.
  • Validate data using assertions.
  • Handle arrays, objects, and complex JSON structures.

REST Assured’s JSON Path implementation is based on the Jayway JSONPath library, providing a robust way to navigate JSON responses. We’ll use the public API https://jsonplaceholder.typicode.com, which returns structured JSON data, for our examples.

Setting Up for JSON Path Testing

To use JSON Path, we’ll set up a Maven project with REST Assured, JUnit, and Allure for reporting. The setup is consistent with previous posts in the series.

Here’s the pom.xml, styled with the requested Blogger format for XML syntax highlighting:


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>rest-assured</artifactId>
    <version>1.0</version>

    <properties>
        <java.version>11</java.version>
        <allure.version>2.27.0</allure.version>
        <aspectj.version>1.9.22</aspectj.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>5.4.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest</artifactId>
            <version>2.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.qameta.allure</groupId>
            <artifactId>allure-junit5</artifactId>
            <version>${allure.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.2.5</version>
                <configuration>
                    <argLine>
                        -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                    </argLine>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjweaver</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>io.qameta.allure</groupId>
                <artifactId>allure-maven</artifactId>
                <version>2.12.0</version>
                <configuration>
                    <reportVersion>${allure.version}</reportVersion>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

No additional setup is needed for https://jsonplaceholder.typicode.com, as it provides rich JSON responses for JSON Path testing.

Using JSON Path in REST Assured

Let’s explore how to use JSON Path to extract and validate data from JSON responses in various scenarios.

Example 1: Basic JSON Path Extraction

Extract a single field from a JSON response using JSON Path and validate its value.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("JSON Path Testing")
public class BasicJsonPathTest {

    @Test
    @Description("Extract and validate a single field using JSON Path")
    public void testExtractPostTitle() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .log().all()
            .when()
                .get("/posts/1")
            .then()
                .statusCode(200)
                .body("title", notNullValue())
                .body("title", isA(String.class));
    }
}

Explanation:

  • body("title", notNullValue()): Uses JSON Path title to validate the title field is not null.
  • isA(String.class): Ensures the title is a string.
  • The JSON Path title directly accesses the top-level title field in the response.
Important: JSON Path expressions in REST Assured are intuitive, using dot notation (e.g., userId) or array indices (e.g., posts[0]) to navigate JSON structures.

Example 2: Navigating Nested JSON Structures

Use JSON Path to extract and validate nested fields in a complex JSON response.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.path.json.JsonPath;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.junit.jupiter.api.Assertions.*;

@Feature("JSON Path Testing")
public class NestedJsonPathTest {

    @Test
    @Description("Extract and validate nested fields using JSON Path")
    public void testExtractNestedFields() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        String response = given()
            .log().all()
            .when()
                .get("/users/1")
            .then()
                .statusCode(200)
                .extract().asString();

        JsonPath jsonPath = new JsonPath(response);
        String companyName = jsonPath.getString("company.name");
        String city = jsonPath.getString("address.city");

        assertEquals("Romaguera-Crona", companyName);
        assertNotNull(city);
    }
}

Explanation:

  • JsonPath jsonPath = new JsonPath(response): Creates a JsonPath object from the response string.
  • getString("company.name"): Extracts the nested name field under company.
  • getString("address.city"): Extracts the nested city field under address.
  • Assertions verify the extracted values.

Sample Response Structure:


{
  "id": 1,
  "name": "Leanne Graham",
  "address": {
    "city": "Gwenborough",
    "street": "Kulas Light"
  },
  "company": {
    "name": "Romaguera-Crona"
  }
}

Example 3: Handling JSON Arrays with JSON Path

Use JSON Path to extract and validate elements from JSON arrays.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("JSON Path Testing")
public class JsonArrayPathTest {

    @Test
    @Description("Validate elements in a JSON array using JSON Path")
    public void testExtractArrayElements() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .log().all()
            .when()
                .get("/posts")
            .then()
                .statusCode(200)
                .body("[0].id", equalTo(1))
                .body("[0].title", notNullValue())
                .body("size()", greaterThan(0));
    }
}

Explanation:

  • [0].id: Accesses the id of the first post in the array.
  • [0].title: Validates the title of the first post is not null.
  • size(): Checks the array length is greater than zero.

Sample Response Structure:


[
  {
    "id": 1,
    "title": "sunt aut facere...",
    "body": "quia et suscipit..."
  },
  {
    "id": 2,
    "title": "qui est esse...",
    "body": "est rerum tempore..."
  }
]
Pro Tip: Use JSON Path’s array notation (e.g., [0], [*]) to handle lists of objects efficiently.

Example 4: Combining JSON Path with Response Specification

Integrate JSON Path validations with a ResponseSpecification for reusable tests.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.specification.ResponseSpecification;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("JSON Path Testing")
public class JsonPathWithSpecTest {

    private ResponseSpecification responseSpec;

    @BeforeEach
    public void setup() {
        responseSpec = new ResponseSpecBuilder()
            .expectStatusCode(200)
            .expectContentType("application/json")
            .expectBody("id", notNullValue())
            .expectBody("title", notNullValue())
            .build();
    }

    @Test
    @Description("Validate JSON Path with Response Specification")
    public void testJsonPathWithSpec() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .log().all()
            .when()
                .get("/posts/1")
            .then()
                .spec(responseSpec)
                .body("userId", equalTo(1));
    }
}

Explanation:

  • responseSpec: Includes JSON Path validations for id and title.
  • spec(responseSpec): Applies the specification to the test.
  • Additional JSON Path validation (userId) is test-specific.

Integrating with Allure Reporting

Document JSON Path tests with Allure, attaching extracted data and response details.


import io.qameta.allure.Allure;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("JSON Path Testing")
public class AllureJsonPathTest {

    @Test
    @Description("Validate JSON Path with Allure reporting")
    public void testJsonPathWithAllure() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        Response response = given()
            .log().all()
            .when()
                .get("/posts/1")
            .then()
                .statusCode(200)
                .body("title", notNullValue())
                .extract().response();

        JsonPath jsonPath = response.jsonPath();
        String title = jsonPath.getString("title");
        Allure.addAttachment("Extracted Title", "text/plain", title, ".txt");
        Allure.addAttachment("Response Body", "application/json", response.asString(), ".json");
    }
}

Explanation:

  • response.jsonPath(): Creates a JsonPath object from the response.
  • Allure.addAttachment: Attaches the extracted title and full response body to the Allure report.
  • Run mvn clean test and mvn allure:serve to view the report.

Integrating with CI/CD

Add JSON Path tests to a GitHub Actions pipeline for automated execution.

Create or update .github/workflows/ci.yml:


name: REST Assured CI Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up JDK 11
        uses: actions/setup-java@v4
        with:
          java-version: '11'
          distribution: 'temurin'

      - name: Cache Maven dependencies
        uses: actions/cache@v4
        with:
          path: ~/.m2/repository
          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
          restore-keys: ${{ runner.os }}-maven-

      - name: Run tests
        run: mvn clean test

      - name: Generate Allure report
        run: mvn allure:report

      - name: Upload Allure results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: allure-results
          path: target/allure-results

      - name: Publish Allure report
        if: always()
        uses: simple-elf/allure-report-action@v1.7
        with:
          allure_results: target/allure-results
          gh_pages: gh-pages
          allure_report: allure-report

      - name: Deploy report to GitHub Pages
        if: always()
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: allure-report

Explanation:

  • Runs JSON Path tests with mvn clean test.
  • Publishes Allure reports to GitHub Pages for visibility.

Tips for Beginners

  • Learn JSON Path Syntax: Master dot notation (e.g., company.name) and array access (e.g., [0]) for effective queries.
  • Inspect Responses: Log responses with log().all() to understand JSON structures before writing JSON Path expressions.
  • Use Assertions: Combine JSON Path with Hamcrest matchers for robust validations.
  • Handle Complex JSON: Use JsonPath objects for advanced extractions in nested or array-heavy responses.
Troubleshooting Tip: If a JSON Path expression fails, verify the response structure using log().all() and check the path syntax against the JSON hierarchy.

What’s Next?

This post enhances our REST Assured series by introducing JSON Path, a key tool for JSON response validation. To continue your learning, consider exploring:

  • GraphQL Testing: Testing GraphQL APIs with REST Assured.
  • Advanced Allure Reporting: Customizing reports with environments and categories.
  • End-to-End Testing: Combining REST Assured with Selenium for UI and API testing.
Stay tuned for more testing tutorials!