Introduction

In our REST Assured series, we’ve explored topics like XML Path Testing, JSON Path Testing, and Status Code Validation, mastering various API testing techniques. Now, we’ll dive into GPath Expressions, a versatile feature in REST Assured for querying and validating data from both JSON and XML responses using a unified syntax. This guide demonstrates how to use GPath to navigate and test response structures, ensuring robust API testing. It’s designed for beginners and experienced developers, with clear explanations and practical examples.

Key Point: GPath Expressions in REST Assured provide a flexible, Groovy-based syntax to query JSON and XML responses, simplifying data extraction and validation across different response formats.

What are GPath Expressions?

GPath Expressions are a Groovy-based query language used in REST Assured to navigate and extract data from JSON and XML responses. GPath combines elements of JSON Path and XPath, offering a consistent syntax for both formats. Key features include:

  • Unified Syntax: Use similar expressions for JSON and XML, reducing the learning curve.
  • Flexible Navigation: Access elements, attributes, arrays, and nested structures easily.
  • Powerful Assertions: Validate extracted data using Hamcrest matchers or JUnit assertions.

REST Assured’s GPath support leverages Groovy’s GPath engine, making it ideal for APIs returning mixed response types. We’ll use https://jsonplaceholder.typicode.com for JSON examples and a mock XML response for XML examples, simulating a typical API scenario.

Setting Up for GPath Testing

To use GPath, we’ll set up a Maven project with REST Assured, JUnit, and Allure for reporting. No additional dependencies are needed for GPath, as it’s built into REST Assured.

Here’s the pom.xml, styled with your preferred 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-tests</artifactId>
    <version>1.0-SNAPSHOT</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>

For JSON examples, we’ll use https://jsonplaceholder.typicode.com. For XML, we’ll use a mock response, as public XML-based REST APIs are less common. In production, target XML APIs or set up a mock server (e.g., WireMock).

Using GPath Expressions in REST Assured

Let’s explore how to use GPath to query and validate JSON and XML responses in various scenarios.

Example 1: Basic GPath Extraction for JSON

Extract and validate a single field from a JSON response using GPath.


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("GPath Expressions Testing")
public class BasicJsonGPathTest {

    @Test
    @Description("Extract and validate a JSON field using GPath")
    public void testExtractJsonTitle() {
        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 the GPath expression title to validate the title field in JSON.
  • isA(String.class): Ensures the field is a string.
  • GPath uses dot notation for JSON, similar to JSON Path.
Important: GPath expressions for JSON are nearly identical to JSON Path expressions in REST Assured, using dot notation (e.g., userId) or array indexing (e.g., [0]).

Example 2: GPath for Nested JSON Structures

Extract and validate nested fields in a 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("GPath Expressions Testing")
public class NestedJsonGPathTest {

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

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

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

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

Sample Response JSON:


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

Explanation:

  • gPath.getString("company.name"): Extracts the nested name under company.
  • gPath.getString("address.city"): Extracts the nested city under address.
  • Assertions verify the extracted values.

Example 3: GPath for XML Responses

Extract and validate data from a mock XML response using GPath.

Assume an endpoint /posts/1 returns:


<post>
    <id>1</id>
    <title>Sample Post</title>
    <author>
        <name>John Doe</name>
    </author>
</post>

import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.junit.jupiter.api.Assertions.*;

@Feature("GPath Expressions Testing")
public class XmlGPathTest {

    @Test
    @Description("Extract and validate XML fields using GPath")
    public void testExtractXml() {
        String mockXml = "1Sample PostJohn Doe";

        Response response = given()
            .contentType("application/xml")
            .body(mockXml)
            .when()
                .post("/echo")
            .then()
                .statusCode(200)
                .extract().response();

        String title = response.path("post.title");
        String authorName = response.path("post.author.name");

        assertEquals("Sample Post", title);
        assertEquals("John Doe", authorName);
    }
}

Explanation:

  • response.path("post.title"): Uses GPath to extract the title element.
  • response.path("post.author.name"): Extracts the nested name under author.
  • GPath for XML uses dot notation, similar to JSON, making it intuitive.
Pro Tip: GPath’s unified syntax for JSON and XML reduces the need to switch between JSON Path and XPath, streamlining test code.

Example 4: GPath with Arrays

Query arrays in a JSON response using GPath.


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("GPath Expressions Testing")
public class ArrayGPathTest {

    @Test
    @Description("Validate JSON array elements using GPath")
    public void testExtractJsonArray() {
        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 is not null.
  • size(): Checks the array length.

Example 5: Combining GPath with Response Specification

Integrate GPath with a ResponseSpecification for reusable validations.


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("GPath Expressions Testing")
public class GPathWithSpecTest {

    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 GPath with Response Specification")
    public void testGPathWithSpec() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

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

Explanation:

  • responseSpec: Uses GPath to validate id and title.
  • spec(responseSpec): Applies the specification.
  • Additional GPath validation (userId) is test-specific.

Integrating with Allure Reporting

Document GPath tests with Allure, attaching extracted data and responses.


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

@Feature("GPath Expressions Testing")
public class AllureGPathTest {

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

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

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

Explanation:

  • response.path("title"): Extracts the title using GPath.
  • Allure.addAttachment: Attaches the extracted title and response body.
  • Run mvn clean test and mvn allure:serve to view the report.

Integrating with CI/CD

Add GPath tests to a GitHub Actions pipeline.

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 GPath tests with mvn clean test.
  • Publishes Allure reports to GitHub Pages.

Tips for Beginners

  • Master GPath Syntax: Learn dot notation (e.g., company.name) and array access (e.g., [0]) for both JSON and XML.
  • Log Responses: Use log().all() to inspect response structures before writing GPath expressions.
  • Use Assertions: Combine GPath with Hamcrest matchers for robust validations.
  • Test Both Formats: Practice with JSON and XML responses to leverage GPath’s flexibility.
Troubleshooting Tip: If a GPath expression fails, verify the response structure with log().all() and check the expression against the JSON or XML hierarchy.

What’s Next?

This post enhances our REST Assured series by introducing GPath Expressions, a unified tool for querying JSON and XML. 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!