API Contract Testing with JSON Schema in Robot Framework
Catch silent API changes before they reach production, with one reusable keyword
Most API tests check the status code and a few fields. That catches loud failures. It misses the quiet ones: a field that changes from a number to a string, a field that disappears, or a new null where your app expects a value. These changes pass a basic test and then break the UI or another service.
Get Rudra Surti’s stories in your inbox
Join Medium for free to get updates from this writer.
Contract testing fixes this. You describe the shape of the response once, as a JSON Schema, and every test checks the full response against it. In this article I show how I do it in Robot Framework, using code from my open-source project Robot-Framework-API-Automation.
1. Add one library
You only need the jsonschema Python package next to RequestsLibrary:
robotframework>=7.0
robotframework-requests>=0.9.7
jsonschema>=4.02. Write the schema
Keep schemas in a schemas/ folder, one file per response. Mark the fields that must exist as required, and give every field its type:
{
"type": "object",
"required": ["firstname", "lastname", "totalprice", "depositpaid", "bookingdates"],
"properties": {
"firstname": {"type": "string"},
"lastname": {"type": "string"},
"totalprice": {"type": "integer"},
"depositpaid": {"type": "boolean"},
"bookingdates": {
"type": "object",
"required": ["checkin", "checkout"],
"properties": {
"checkin": {"type": "string", "format": "date"},
"checkout": {"type": "string", "format": "date"}
}
}
}
}3. One reusable keyword
This keyword loads a schema file and validates any response body against it. If anything does not match, the test fails with a clear message that names the field.
*** Settings ***
Library RequestsLibrary
Library OperatingSystem*** Keywords ***
Response Should Match Schema
[Arguments] ${body} ${schema_file}
${path}= Normalize Path ${CURDIR}/../schemas/${schema_file}
${schema}= Evaluate json.load(open($path)) modules=json
Evaluate jsonschema.validate(instance=$body, schema=$schema) modules=jsonschemaTip: Normalize Path comes from the OperatingSystem library. If you forget to import it, the dry run fails with "No keyword with name 'Normalize Path'".
4. Use it in tests
*** Test Cases ***
Get Booking Matches Contract
[Tags] contract
${id}= Create Booking
${resp}= GET On Session api /booking/${id}
Response Should Match Schema ${resp.json()} booking.jsonThe test now reads like a sentence, and it checks every field and type in one line.
5. Add data-driven cases
Robot Framework’s Test Template lets one keyword run many input rows. I use it to send several booking variations and check that each response still matches the contract:
*** Settings ***
Test Template Booking Should Follow Contract*** Test Cases *** FIRST LAST PRICE DEPOSIT
Standard booking Rudra Surti 150 True
No deposit Asha Patel 90 False
Zero price Dev Shah 0 True6. Run it in CI
Tag the tests contract so you can run them alone with robot --include contract tests/. In my project, GitHub Actions runs all 21 tests on every push and uploads the HTML report.
What I learned
- Keep schemas small and inline. Shared
$reffiles add setup work for little gain in a test project. - Check types, not only presence. Most real bugs I find are a number that became a string.
- Run contract tests early in the pipeline. They are fast and catch breaking changes before the slower UI tests.
I use Claude to draft schemas from sample responses, then I review every field by hand. It saves time, but the review is what makes the contract right.
The full code is on GitHub: Robot-Framework-API-Automation. More about my work: rudrasurti.github.io.









