Schema Validation in KarateBDD using a Reusable Java Method


❓ Why Validating Schema for API Tests is Important?
Schema validation is more than a "nice-to-have" — it’s a critical guardrail for modern API testing. Here's why we need it :
🔥 Top 5 Reasons to Validate JSON Schema in API Testing
✅ Catch Contract Breaks Early
Changes in field names, data types, or required fields often break consumers downstream. Schema validation acts as a first line of defense.🕊 Ensure Backward Compatibility
In microservices and public APIs, maintaining consistent structure across versions is crucial. Schema validation ensures your responses remain predictable.🚀 Boost Confidence in CI/CD Deployments
Automated schema checks in your pipeline prevent broken APIs from being promoted to higher environments or production.🧠 Validate Optional and Nested Structures
Complex APIs often include optional fields, polymorphic types, or deeply nested structures. JSON schema handles these scenarios with precision.🧯 Reduce Regression Bugs
Developers may unknowingly alter response structures. Schema tests catch such regressions even when functional outputs still "look correct."
💬 Real-World Scenarios
🛳 For a Global Shipping Platform
“Schema validation saved us from shipping an API that silently dropped a required field after a refactor. Functional tests passed, but schema validation caught it — and saved our clients from breaking.”
🏥 For a Healthcare SaaS Provider
“Our APIs deal with EHRs and HL7/FHIR data. We thought integration tests were enough, but schema validation caught malformed fields that would have caused downstream rejections by insurance providers.”
🛒 For an E-Commerce Checkout Service
“We introduced a ‘discountCode’ field, but a typo in the payload caused it to go missing for 10% of orders. Schema tests added in staging picked it up, and we avoided hundreds of support calls post-deploy.”
📚 Table of Contents
1. 🔧 Prerequisites
Before we begin, make sure you have the following setup on your machine:
Requirement | Details |
☑️ Java | JDK 11 or later installed and added to your PATH |
☑️ Maven | Installed and working (mvn -v should succeed) |
☑️ KarateBDD | A Karate project scaffolded and ready to run tests |
☑️ IDE or Editor | IntelliJ IDEA, VSCode, or any preferred IDE |
☑️ Internet Access | Required to download Maven dependencies |
☑️ Resource Folder | Create a folder like src/test/resources/schema/ for schema files |
Note: KarateBDD Maven Quickstart setup:
Set up a Karate Maven project using the official quickstart guide. It helps scaffold your project correctly with all necessary dependencies and structure.
📦 Step 1: Add Maven Dependencies
Add these dependencies to your pom.xml
:
<dependency>
<groupId>com.github.erosb</groupId>
<artifactId>everit-json-schema</artifactId>
<version>1.14.1</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20211205</version>
</dependency>
🧠 Step 2: Create Reusable Java Method
Create JsonSchemaValidator.java
in your utils/
directory:
package utils;
import org.everit.json.schema.Schema;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.json.JSONArray;
import org.json.JSONTokener;
import java.io.InputStream;
public class JsonSchemaValidator {
public static boolean validate(String schemaPath, String jsonData) {
try (InputStream schemaInputStream = JsonSchemaValidator.class.getResourceAsStream(schemaPath)) {
if (schemaInputStream == null) {
throw new RuntimeException("Schema file not found: " + schemaPath);
}
JSONObject rawSchema = new JSONObject(new JSONTokener(schemaInputStream));
Schema schema = SchemaLoader.load(rawSchema);
String trimmedJsonData = jsonData.trim();
if (trimmedJsonData.startsWith("[")) {
schema.validate(new JSONArray(trimmedJsonData));
} else if (trimmedJsonData.startsWith("{")) {
schema.validate(new JSONObject(trimmedJsonData));
} else {
throw new RuntimeException("Invalid JSON data format.");
}
return true;
} catch (Exception e) {
throw new RuntimeException("Schema validation failed for data: '" +
jsonData.substring(0, Math.min(jsonData.length(), 100)) + "...'. Error: " + e.getMessage(), e);
}
}
}
🧾 Step 3: Create JSON Schema File
Save this as products-schema.json
under src/test/resources/schema/
:
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "array",
"items": {
"$ref": "#/definitions/Welcome10Element"
},
"definitions": {
"Welcome10Element": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"format": "integer"
},
"name": {
"type": "string"
},
"data": {
"anyOf": [
{
"$ref": "#/definitions/Data"
},
{
"type": "null"
}
]
}
},
"required": [
"data",
"id",
"name"
],
"title": "Welcome10Element"
},
"Data": {
"type": "object",
"additionalProperties": false,
"properties": {
"color": {
"type": "string"
},
"capacity": {
"type": "string"
},
"capacity GB": {
"type": "integer"
},
"price": {
"type": "number"
},
"generation": {
"type": "string"
},
"year": {
"type": "integer"
},
"CPU model": {
"type": "string"
},
"Hard disk size": {
"type": "string"
},
"Strap Colour": {
"type": "string"
},
"Case Size": {
"type": "string"
},
"Color": {
"type": "string"
},
"Description": {
"type": "string"
},
"Capacity": {
"type": "string"
},
"Screen size": {
"type": "number"
},
"Generation": {
"type": "string"
},
"Price": {
"type": "string"
}
},
"required": [],
"title": "Data"
}
}
}
Note: If you have not being officially given any schema as part of the requirements, you can generate the schema using online tools like json-formatter
🧪 Step 4: Call Validator in Karate Feature File
Feature: Validate API response schema
Background:
Given url 'https://api.restful-api.dev'
* def validator = Java.type('utils.JsonSchemaValidator')
Scenario: Validate schema
Given path '/objects'
When method GET
Then status 200
* string responseString = karate.toJson(response)
* def matchingSchema = validator.validate('/resources/schema/products-schema.json',responseString)
And match matchingSchema == true
Note: When comparing json , if you get following error, keep in the mind the following when passing response object to call the “validate” method for comparison
>>>> 01: validator.validate('/resources/schema/products-schema.json',responseString) <<<< org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (validate) on utils.JsonSchemaValidator failed due to: Cannot convert '[{"id":"1","name":"Google Pixel 6 Pro","data":{"color":"Cloudy White","capacity":"128 GB"}},{"id":"2","name":"Apple iPhone 12 Mini, 256GB...'(language: Java, type: com.intuit.karate.graal.JsList) to Java type 'java.lang.String': Invalid or lossy primitive coercion. - <js>.:program(Unnamed:1)
* def responseString = karate.toJson(response) Vs * string responseString = karate.toJson(response)
In Karate,
def
is used for JavaScript variable assignments.When
response
is a JSON array (as suggested by theJsList
type and the example data[{...}, {...}]
),karate.toJson(response)
might convert the Java/Karate representation of the response into a JavaScript array/list object within the GraalVM JavaScript engine that Karate uses.This JavaScript array is then seen by the Java side (your
JsonSchemaValidator
) as acom.intuit
.karate.graal.JsList
, not as a plainjava.lang.String
that contains the JSON text.Therefore, when
validator.validate
(which is a Java method) tries to use thisJsList
as aString
, it fails with the "Cannot convert" error because there's no direct, implicit conversion from aJsList
to aString
in a way that would produce the JSON string representation.
>>>>
01: validator.validate('/resources/schema/products-schema.json',responseString)
<<<<
org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (validate) on utils.JsonSchemaValidator failed due to: Cannot convert '[{"id":"1","name":"Google Pixel 6 Pro","data":{"color":"Cloudy White","capacity":"128 GB"}},{"id":"2","name":"Apple iPhone 12 Mini, 256GB...'(language: Java, type: com.intuit.karate.graal.JsList) to Java type 'java.lang.String': Invalid or lossy primitive coercion.
- <js>.:program(Unnamed:1)
Subscribe to my newsletter
Read articles from Himanshu Pandey directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
