Skip to main content

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

Karate provides built-in markers for type and format validation:

MarkerDescription
#ignoreSkip comparison for this field
#nullValue must be null (key must be present)
#notnullValue must not be null
#presentKey must be present (value can be anything, even null)
#notpresentKey must not be present at all
#arrayValue must be a JSON array
#objectValue must be a JSON object
#booleanValue must be true or false
#numberValue must be a number
#stringValue must be a string
#uuidValue must match UUID format
#regex STRValue must match the regular expression
#? EXPRJavaScript expression must evaluate to true
Gherkin
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'
}
"""
Regex Escaping

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):

Gherkin
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:

Gherkin
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:

Gherkin
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:

Gherkin
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:

Gherkin
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:

Gherkin
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:

Gherkin
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:

Gherkin
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:

Gherkin
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:

Gherkin
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 Reference
SymbolMeaning
$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:

Gherkin
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:

Gherkin
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:

Gherkin
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:

SymbolEquivalent
^contains
^^contains only
^*contains any
^+contains deep
!^not contains
Gherkin
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:

Gherkin
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