JSON Schema Validation with REST Assured: Ensuring API Data Integrity

Introduction

In our REST Assured series, we’ve explored topics like File Uploads and File Downloads, Multi-Part Form Data, and Filters, building a robust foundation for API testing. Now, we’ll dive into JSON Schema Validation, a powerful technique to verify that API responses conform to expected JSON structures. This guide demonstrates how to use REST Assured to validate JSON responses against a schema, with practical examples and best practices. It’s designed for beginners and experienced developers, ensuring you can confidently test API data integrity.

Key Point: JSON Schema Validation in REST Assured ensures API responses match predefined data structures, enhancing test reliability and catching data contract violations early.

What is JSON Schema Validation?

JSON Schema is a specification for defining the structure and data types of JSON data. It allows you to specify properties, required fields, data types, and constraints (e.g., string length, number ranges). In API testing, JSON Schema Validation ensures that responses adhere to this schema, catching issues like missing fields, incorrect types, or invalid values.

REST Assured supports JSON Schema Validation through the io.rest-assured.module.jsv module, using the matchesJsonSchemaInClasspath() or matchesJsonSchema() methods. We’ll use https://jsonplaceholder.typicode.com/posts for examples, validating its JSON response against a schema stored in the project’s resources. The schema will define the structure of a post object, ensuring fields like id, userId, title, and body are present and correctly typed.

Setting Up for JSON Schema Validation

We’ll set up a Maven project with REST Assured, JUnit, and Allure for reporting, adding the rest-assured-json-schema-validator dependency for schema validation. The JSON Schema will be stored in src/test/resources/schemas/post-schema.json.

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



    4.0.0
    com.example
    rest-assured-tests
    1.0-SNAPSHOT

    
        11
        2.27.0
        1.9.22
    

    
        
            io.rest-assured
            rest-assured
            5.4.0
            test
        
        
            io.rest-assured
            json-schema-validator
            5.4.0
            test
        
        
            org.junit.jupiter
            junit-jupiter
            5.10.2
            test
        
        
            org.hamcrest
            hamcrest
            2.2
            test
        
        
            com.fasterxml.jackson.core
            jackson-databind
            2.15.2
            test
        
        
            io.qameta.allure
            allure-junit5
            ${allure.version}
            test
        
    

    
        
            
                org.apache.maven.plugins
                maven-surefire-plugin
                3.2.5
                
                    
                        -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                    
                
                
                    
                        org.aspectj
                        aspectjweaver
                        ${aspectj.version}
                    
                
            
            
                io.qameta.allure
                allure-maven
                2.12.0
                
                    ${allure.version}
                
            
        
    

Create a JSON Schema file at src/test/resources/schemas/post-schema.json:


{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "id": { "type": "integer" },
    "userId": { "type": "integer" },
    "title": { "type": "string" },
    "body": { "type": "string" }
  },
  "required": ["id", "userId", "title", "body"],
  "additionalProperties": false
}

This schema defines a post object with required fields and their types, matching the structure of https://jsonplaceholder.typicode.com/posts responses.

Performing JSON Schema Validation

Let’s explore how to validate JSON responses against a schema using REST Assured.

Example 1: Basic JSON Schema Validation

Validate a single post response against the schema.


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 io.restassured.module.jsv.JsonSchemaValidator.*;

@Feature("JSON Schema Validation Testing")
public class BasicSchemaValidationTest {

    @Test
    @Description("Test JSON Schema validation for a single post")
    public void testBasicSchemaValidation() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .log().all()
        .when()
            .get("/posts/1")
        .then()
            .statusCode(200)
            .body(matchesJsonSchemaInClasspath("schemas/post-schema.json"));
    }
}

Explanation:

  • matchesJsonSchemaInClasspath("schemas/post-schema.json"): Validates the response against the schema in the classpath.
  • log().all(): Logs the request and response for debugging.
  • The test fails if the response doesn’t match the schema (e.g., missing fields, wrong types).
Important: Store schemas in src/test/resources to ensure they’re available on the classpath during testing.

Example 2: Validating an Array of Objects

Validate a list of posts against a schema for an array.

Create a schema for an array of posts at src/test/resources/schemas/posts-array-schema.json:


{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "id": { "type": "integer" },
      "userId": { "type": "integer" },
      "title": { "type": "string" },
      "body": { "type": "string" }
    },
    "required": ["id", "userId", "title", "body"],
    "additionalProperties": false
  }
}

Test code:


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 io.restassured.module.jsv.JsonSchemaValidator.*;

@Feature("JSON Schema Validation Testing")
public class ArraySchemaValidationTest {

    @Test
    @Description("Test JSON Schema validation for an array of posts")
    public void testArraySchemaValidation() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .log().all()
        .when()
            .get("/posts")
        .then()
            .statusCode(200)
            .body(matchesJsonSchemaInClasspath("schemas/posts-array-schema.json"));
    }
}

Explanation:

  • The schema defines an array where each item must match the post object structure.
  • Validates that all posts in the response conform to the schema.

Example 3: Combining Schema Validation with Assertions

Combine schema validation with specific field assertions.


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 io.restassured.module.jsv.JsonSchemaValidator.*;
import static org.hamcrest.Matchers.*;

@Feature("JSON Schema Validation Testing")
public class SchemaWithAssertionsTest {

    @Test
    @Description("Test JSON Schema validation with specific assertions")
    public void testSchemaWithAssertions() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .log().all()
        .when()
            .get("/posts/1")
        .then()
            .statusCode(200)
            .body(matchesJsonSchemaInClasspath("schemas/post-schema.json"))
            .body("id", equalTo(1))
            .body("title", notNullValue());
    }
}

Explanation:

  • Combines matchesJsonSchemaInClasspath() with Hamcrest assertions for specific fields.
  • Ensures both structural and value-based validation.
Pro Tip: Use schema validation for structure and Hamcrest for specific value checks to balance thoroughness and flexibility.

Example 4: Schema Validation with Authentication

Validate a response from an authenticated endpoint using a mock Bearer token.


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 io.restassured.module.jsv.JsonSchemaValidator.*;

@Feature("JSON Schema Validation Testing")
public class AuthenticatedSchemaValidationTest {

    @Test
    @Description("Test JSON Schema validation with Bearer token")
    public void testAuthenticatedSchemaValidation() {
        RestAssured.baseURI = "https://httpbin.org";

        // Mock authenticated request
        given()
            .header("Authorization", "Bearer my-bearer-token")
            .log().all()
        .when()
            .get("/get")
        .then()
            .statusCode(200)
            .body(matchesJsonSchemaInClasspath("schemas/httpbin-get-schema.json"))
            .body("headers.Authorization", equalTo("Bearer my-bearer-token"));
    }
}

Create a schema for the /get response at src/test/resources/schemas/httpbin-get-schema.json:


{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "args": { "type": "object" },
    "headers": {
      "type": "object",
      "properties": {
        "Authorization": { "type": "string" }
      }
    },
    "url": { "type": "string" }
  },
  "required": ["args", "headers", "url"],
  "additionalProperties": true
}

Explanation:

  • Uses a mock Bearer token for authentication.
  • Validates the response structure for the /get endpoint, which includes headers and URL.
  • Ensures the Authorization header is present in the response.

Example 5: Schema Validation with Response Specification

Use a ResponseSpecification to reuse schema validation logic.


import io.qameta.allure.Description;
import io.qameta.allure.*;
import io.restassured.RestAssured;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.specification.ResponseSpecification;
import org.junit.jupiter.api.*;
import static io.restassured.RestAssured.*;

import static io.restassured.module.jsv.JsonSchemaValidator.*;
import static org.hamcrest.Matchers.*;

@Feature("JSON Schema Validation Testing")
public class SchemaWithSpecTest {

    private ResponseSpecification postSchemaSpec;

    @BeforeEach
    public void setup() {
        postSchemaSpec = new ResponseSpecBuilder()
            .expectStatusCode(200)
            .expectBody(matchesJsonSchemaInClasspath("post-schema.json"))
            .build();
    }

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

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

Explanation:

  • postSchemaSpec: Defines a specification that includes schema validation.
  • spec(postSchemaSpec): Applies the specification for reusable validation.
  • Adds test-specific assertions for the id field.

Integrating with Allure Reporting

Document schema validation tests with Allure, attaching request and schema details.


import io.qameta.allure.*;
import io.restassured.*;
import io.restassured.response.Response;
import org.junit.jupiter.api.*;
import static io.restassured.RestAssured.*;
import static io.restassured.module.jsv.JsonSchemaValidator.*;

@Feature("JSON Schema Validation Testing")
public class AllureSchemaTest {

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

        Response response = given()
            .log().all()
        .when()
            .get("/posts/1")
        .then()
            .statusCode(200)
            .body(matchesJsonSchemaInClasspath("schemas/post-schema.json"))
            .extract().response();

        Allure.addAttachment("Response Body", "application/json", response.asString(), ".json"));
        // Attach schema file to allure
        try (InputStream schemaStream = getClass().getResourceAsStream("/schemas/post-schema.json")) {
            Allure.addAttachment("JSON Schema", "application/json", new String(schemaStream.readAllBytes()), ".json");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Explanation:

  • Allure.addAttachment: Attaches the response body and schema file to the Allure report.
  • mvn clean test and mvn allure:serve to to view the the report.

Integrating with CI/CD Pipeline

Add schema validation tests to a GitHub Actions pipeline for automation.

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


name: REST Assured CI

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@v3
        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

pExplanation:

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

Tips for Beginners

  • Create Simple Schemas: Start with basic schemas and add constraints gradually (e.g., minLength, pattern).
  • Store Schemas in Resources: Keep schemas in src/test/resources for easy classpath access.
  • Debug Schema Failures: Use log().all() to inspect responses and schema error messages for mismatches.
  • Reuse Schemas: Define schemas for common endpoints and reuse them with ResponseSpecs.

pTroubleshooting Tip: If schema validation fails, check the schema for errors (e.g., typos, missing fields) and enable verbose logging to compare the response JSON with the schema requirements.

What’s Next?

This post enhances our REST Assured series by introducing JSON Schema Validation, a critical tool for ensuring API data integrity. 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!