The API Testing Masterclass lesson on JSON Schema validation showed you how to define an expected response shape in a .json file and validate the response against it. In Rest Assured that means importing io.rest-assured:json-schema-validator, writing a separate schema file, and calling .body(matchesJsonSchemaInClasspath("user-schema.json")). In Karate, schema validation is inline — you write the expected shape directly in the scenario using fuzzy markers, and the match keyword enforces it. No extra library, no separate file (unless you want one).
Inline schema validation
The simplest form: a match block that describes the expected shape of the entire response:
Scenario: Validate user response schema
Given path 'users', 1
When method get
Then status 200
And match response ==
"""
{
id: '#number',
name: '#string',
email: '#regex .+@.+\\..+',
role: '#? _ == "admin" || _ == "tester" || _ == "viewer"',
active: '#boolean',
createdAt: '#string',
address: {
street: '#string',
city: '#string',
postcode: '#string'
},
tags: '#array',
phone: '##string'
}
"""The triple-quoted """ block is Karate's multiline string syntax — it makes complex match expressions readable. Each field uses a fuzzy marker: #number, #string, #boolean, #array, #regex, #?, or ##string for optional fields.
This single match block does what a JSON Schema file does, in-place, with no import.
#? — custom validation expressions
#? lets you write any boolean JavaScript expression as a validation rule. The _ placeholder is the current field value:
# Role must be one of three allowed values
role: '#? _ == "admin" || _ == "tester" || _ == "viewer"'
# ID must be a positive integer
id: '#? _ > 0'
# Name must be non-empty
name: '#? _.length > 0'
# Price must be within range
price: '#? _ >= 0 && _ <= 10000'#? is Karate's equivalent of a JSON Schema enum or minimum/maximum constraint, expressed as readable JavaScript.
Schema validation for arrays — match each
When the response is a list, match each applies the schema to every element:
Scenario: Validate all users match the schema
Given path 'users'
When method get
Then status 200
And match each response ==
"""
{
id: '#number',
name: '#string',
email: '#string',
username: '#string'
}
"""One assertion. Every object in the list is validated against the schema. If any user is missing a field or has the wrong type, the assertion fails and the failure message identifies which element and which field broke.
Reusable schema — def
When the same schema appears in multiple scenarios, extract it into a def:
Background:
* url baseUrl
* def userSchema =
"""
{
id: '#number',
name: '#string',
email: '#string',
role: '#string'
}
"""
Scenario: Single user matches schema
Given path 'users', 1
When method get
Then status 200
And match response == userSchema
Scenario: All users match schema
Given path 'users'
When method get
Then status 200
And match each response == userSchemaThe schema defined in Background is available to all scenarios in the file. Both the single-object and array assertions reuse the same definition — change the schema once and both tests update.
Schema from a file
For large or complex schemas shared across multiple feature files, store the schema in a JSON file and load it with read():
* def userSchema = read('classpath:schemas/user-schema.json')
Then match response == userSchemauser-schema.json uses the same fuzzy marker syntax as an inline match:
{
"id": "#number",
"name": "#string",
"email": "#regex .+@.+\\..+",
"role": "#string",
"active": "#boolean"
}This is the closest Karate equivalent to the JSON Schema approach. The format is simpler than JSON Schema's draft specification, and you can use it with match == or match each identically to an inline assertion.
Karate inline schema vs JSON Schema file
Schema validation: JSON Schema file vs Karate inline match
JSON Schema (Rest Assured)
Separate .json file per resource
Stored in src/test/resources/schemas/
Verbose draft-07 syntax
type, properties, required arrays, $ref for reuse
Imported via json-schema-validator
Extra Maven dependency required
Industry standard — works with any language
Supported by Postman, Pact, OpenAPI
Karate match markers
Inline in the feature file
No separate file needed for simple schemas
Compact marker syntax
#string, #number, #regex, #?, ## for optional
Zero extra dependencies
match is a built-in keyword
File-based schemas also supported
read('classpath:schemas/user.json') for sharing
⚠️ Common mistakes
- Using
match ==when you only care about a subset of fields. If the response has 15 fields and your schema specifies 5,match ==fails because of the 10 unexpected fields. Usematch containsfor partial schema validation andmatch ==only when you want to assert the exact, complete shape. - Forgetting to escape regex backslashes. In a Karate string,
\\.is a literal\.in the regex (matching any character followed by a dot). A common mistake: writing#regex .+@.+\.+(one backslash) instead of#regex .+@.+\\..+(double backslash, matching a literal dot followed by more characters). Test your regex patterns against a real response before committing. - Defining the schema inside a scenario instead of Background when it's shared. A schema defined with
definside a scenario is only available in that scenario. If three scenarios all validate against the same schema, define it once in Background or in a shared JSON file. Duplicated inline schemas drift apart over time.
🎯 Practice task
Write schema validation at three levels of reuse. 35–45 minutes.
- GET
/users/1from JSONPlaceholder. Write an inlinematch response ==with the triple-quoted block. Use#numberforid,#stringfornameandemail, and##stringforphoneandwebsite(optional). Run green. - Add a
#?assertion:id: '#? _ > 0'andname: '#? _.length > 0'. Verify both markers pass. Forceid: '#? _ > 100'— confirm the failure message shows the actual id value. - GET
/usersand writematch each response ==using a schema that coversid,name,email, andusername. Confirm it runs across all 10 users in the list. - Extract the schema into
Backgroundas adef. Rewrite both the single-user and multi-user scenarios to referenceuserSchema. Confirm both still pass. - Create a file
src/test/java/schemas/user-schema.jsonwith the fuzzy-marker schema. Load it with* def userSchema = read('classpath:schemas/user-schema.json')in Background. Delete the inlinedefand confirm the tests still pass — the external file is now the single source of truth. - Negative schema test: write a scenario that POSTs to
/userswith a body missing theemailfield. If the API returns a 400 or 422, validate the error response schema:match response contains { error: '#string' }or similar (JSONPlaceholder won't return this, but write it as if the API does — practice the pattern). - Stretch: look at the API Testing Masterclass lesson on JSON Schema validation and compare the JSON Schema draft-07 syntax for the same user schema you wrote in step 1. Note which fields map to
type: string, which map topattern, and which would needoneOffor enum validation. This comparison is the clearest way to see what Karate's markers are replacing.
Next chapter: variables, data, and reuse — reading external files, def in depth, Scenario Outline for data-driven testing, and calling feature files from other feature files.