ASSERTIONS
Schema Validation
Validate complex API payloads using Karate's schema validation - simpler and more powerful than JSON-schema, with zero dependencies and inline assertions in a single step.
On this page:
- Validation markers - Type checking with
#string,#number,#boolean, etc. - Optional fields - Handle missing keys with
##prefix - Array validation - Size and element type checks with
#[] - Self-validation - Custom logic with
#? expression - Reusable schemas - Define once, use everywhere
- Cross-field validation - Validate related fields together
Validation Markers
Karate provides built-in markers for type and format validation:
| Marker | Description |
|---|---|
#ignore | Skip comparison for this field |
#null | Value must be null (key must be present) |
#notnull | Value must not be null |
#present | Key must be present (value can be anything, even null) |
#notpresent | Key must not be present at all |
#array | Value must be a JSON array |
#object | Value must be a JSON object |
#boolean | Value must be true or false |
#number | Value must be a number |
#string | Value must be a string |
#uuid | Value must match UUID format |
#regex STR | Value must match the regular expression |
#? EXPR | JavaScript expression must evaluate to true |
Feature: Validation markers
Scenario: Basic type validation
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200
And match response ==
"""
{
id: '#number',
name: '#string',
username: '#string',
email: '#regex .+@.+',
address: '#object',
phone: '#string',
website: '#string',
company: '#object'
}
"""
Use double backslash for regex escaping: '#regex a\\.dot' matches 'a.dot'
Optional Fields
Prefix any marker with ## to make a field optional (key can be missing or value can be null):
Feature: Optional field validation
Scenario: Schema with optional fields
* def user = { id: 1, name: 'John', email: 'john@test.com' }
# middleName is optional - can be missing or null
* match user == { id: '#number', name: '#string', email: '#string', middleName: '##string' }
Null vs Not Present
Karate distinguishes between a key with null value and a missing key:
Feature: Null handling
Scenario: Understand null vs notpresent
* def withNull = { a: null }
* def empty = { }
# Key exists with null value
* match withNull == { a: '#null' }
* match withNull == { a: '#present' }
# Key does not exist
* match empty == { a: '#notpresent' }
* match empty == { a: '##null' }
# ##null matches both cases
* match withNull == { a: '##null' }
* match empty == { a: '##null' }
Array Validation
Use #[] for array validation with optional size and element type:
Feature: Array validation
Scenario: Array size and type patterns
Given url 'https://jsonplaceholder.typicode.com'
And path 'users'
When method get
Then status 200
# Must be an array (any size)
* match response == '#[]'
# Must be an array of exactly 10 items
* match response == '#[10]'
# Must be an array of objects
* match response == '#[] #object'
Array Element Validation
Combine array markers with element type validation:
Feature: Array element types
Scenario: Validate array element types
* def tags = ['api', 'test', 'karate']
# Array of strings
* match tags == '#[] #string'
# Array of exactly 3 strings
* match tags == '#[3] #string'
# Each string has length > 0
* match tags == '#[] #string? _.length > 0'
Match Each
Use match each to validate every element in an array against a schema:
Feature: Match each validation
Scenario: Validate each array element
Given url 'https://jsonplaceholder.typicode.com'
And path 'users'
When method get
Then status 200
And match each response ==
"""
{
id: '#number',
name: '#string',
username: '#string',
email: '#regex .+@.+',
address: '#object',
phone: '#string',
website: '#string',
company: '#object'
}
"""
Self-Validation Expressions
Use #? EXPR for custom validation logic. The underscore _ represents the current value:
Feature: Self-validation
Scenario: Custom validation with underscore
* def user = { name: 'John', age: 25 }
# Validate age is between 18 and 100
* match user == { name: '#string', age: '#? _ >= 18 && _ <= 100' }
# Combine type check with validation
* match user == { name: '#string? _.length > 0', age: '#number? _ > 0' }
Using Functions
Create reusable validation functions:
Feature: Function validation
Scenario: Validate with functions
* def isValidEmail = function(x) { return x.indexOf('@') > 0 }
* def user = { email: 'test@example.com' }
* match user == { email: '#? isValidEmail(_)' }
Using Variables in Expressions
Reference other variables in validation expressions:
Feature: Variable references
Scenario: Use variables in validation
* def minAge = 18
* def maxAge = 100
* def user = { age: 25 }
* match user == { age: '#? _ >= minAge && _ <= maxAge' }
Cross-Field Validation
Use $ to reference the JSON root for cross-field validation:
Feature: Cross-field validation
Scenario: Validate related fields
* def temperature = { celsius: 100, fahrenheit: 212 }
# Validate fahrenheit equals celsius * 1.8 + 32
* match temperature == { celsius: '#number', fahrenheit: '#? _ == $.celsius * 1.8 + 32' }
Match Each with Parent Reference
In match each, use _$ to reference the current array element:
Feature: Parent reference in match each
Scenario: Cross-field validation in arrays
* def orders =
"""
[
{ items: 3, total: 30, pricePerItem: 10 },
{ items: 5, total: 25, pricePerItem: 5 }
]
"""
# Validate total equals items * pricePerItem
* match each orders contains { total: '#? _ == _$.items * _$.pricePerItem' }
| Symbol | Meaning |
|---|---|
$ | The JSON root document |
_ | The current value being validated ("self") |
_$ | The parent object in match each iterations |
Reusable Schemas
Define schemas as variables and reuse them across scenarios:
Feature: Reusable schemas
Background:
* def geoSchema = { lat: '#string', lng: '#string' }
* def addressSchema = { street: '#string', suite: '#string', city: '#string', zipcode: '#string', geo: '#(geoSchema)' }
* def companySchema = { name: '#string', catchPhrase: '#string', bs: '#string' }
* def userSchema =
"""
{
id: '#number',
name: '#string',
username: '#string',
email: '#regex .+@.+',
address: '#(addressSchema)',
phone: '#string',
website: '#string',
company: '#(companySchema)'
}
"""
Scenario: Apply reusable schema
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200
And match response == userSchema
Embedded Schema References
Use #(schemaName) to embed a schema within another:
Feature: Embedded schemas
Scenario: Nested schema references
* def itemSchema = { id: '#number', name: '#string' }
* def orderSchema = { orderId: '#number', items: '#[] itemSchema' }
* def order = { orderId: 1, items: [{ id: 1, name: 'Widget' }] }
* match order == orderSchema
External Schema Files
Load schemas from JSON files for larger projects:
Feature: External schemas
Scenario: Load schema from file
* def userSchema = read('classpath:schemas/user-schema.json')
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200
And match response == userSchema
Contains Short-Cuts
Use these symbols in embedded expressions for contains-style matching:
| Symbol | Equivalent |
|---|---|
^ | contains |
^^ | contains only |
^* | contains any |
^+ | contains deep |
!^ | not contains |
Feature: Contains short-cuts
Scenario: Use contains in schema validation
* def cat =
"""
{
name: 'Billie',
kittens: [
{ id: 23, name: 'Bob' },
{ id: 42, name: 'Wild' }
]
}
"""
* def expected = [{ id: 42, name: 'Wild' }, { id: 23, name: 'Bob' }]
# Validate kittens contains all expected items in any order
* match cat == { name: 'Billie', kittens: '#(^^expected)' }
Complete Schema Example
Combine all techniques for comprehensive validation:
Feature: Complete schema validation
Scenario: Validate complex API response
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200
And match response ==
"""
{
id: '#number',
name: '#string',
username: '#string? _.length >= 3',
email: '#regex .+@.+\\..+',
address: {
street: '#string',
suite: '#string',
city: '#string',
zipcode: '#regex \\d{5}-\\d{4}',
geo: {
lat: '#string',
lng: '#string'
}
},
phone: '#string',
website: '#string',
company: {
name: '#string',
catchPhrase: '#string',
bs: '#string'
}
}
"""
Next Steps
- Fuzzy Matching - Deep dive into validation markers
- Match Keyword - Core match operations
- Response Validation - Apply schemas to API responses