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.
Why Use Schema Validation
- Simpler than JSON-schema: No external dependencies or complex DSL syntax
- Single-step assertions: Mix type, domain, and business logic validations together
- Flexible patterns: Reusable schemas, conditional validation, cross-field checks
- Better error messages: Clear feedback showing expected vs actual values
Simple Schema Validation
Feature: Simple schema validation
Scenario: Basic type validation
Given url baseUrl
And path 'users', 123
When method get
Then status 200
And match response == {
id: '#number',
name: '#string',
email: '#regex .+@.+',
active: '#boolean'
}
Optional Fields
Feature: Optional field validation
Scenario: Schema with optional fields
Given url baseUrl
And path 'api/profiles'
When method get
Then status 200
And match response == {
id: '#number', # Required
name: '#string', # Required
avatar: '##string', # Optional - can be missing
bio: '##string', # Optional - can be missing
phone: '##null', # Optional - can be null or missing
verified: '#boolean' # Required
}
Array Validation
Feature: Array validation
Scenario: Array size and type patterns
Given url baseUrl
And path 'api/products'
When method get
Then status 200
And match response == {
products: '#[]', # Must be array (any size)
featured: '#[5]', # Must be array of exactly 5 items
categories: '#[] #string', # Array of strings (any size)
topRated: '#[3] #object', # Array of exactly 3 objects
tags: '#[]? _.length > 0' # Custom array validation
}
Array Validation Patterns
- Use
#[]
for any array size - Use
#[N]
for specific size (e.g.,#[5]
) - Use
#[] #string
to validate element types - Use
#[]? expression
for custom validation logic
Array Elements with Schemas
Feature: Array element validation
Scenario: Validate each array element
Given url baseUrl
And path 'api/orders'
When method get
Then status 200
And match each response.orders == {
id: '#number',
customer: '#object',
items: '#[]',
total: '#number? _ > 0',
status: '#regex (pending|confirmed|shipped)'
}
Reusable Schemas
Feature: Reusable schemas
Background:
# Define reusable schemas
* def userSchema = {
id: '#number',
name: '#string',
email: '#regex .+@.+',
active: '#boolean'
}
* def addressSchema = {
street: '#string',
city: '#string',
zipCode: '#regex \d{5}'
}
Scenario: Apply reusable schema
Given url baseUrl
And path 'users', 123
When method get
Then status 200
And match response == userSchema
Scenario: Combine multiple schemas
Given url baseUrl
And path 'users', 123
When method get
Then status 200
And match response == karate.merge(userSchema, {
address: addressSchema,
preferences: { theme: '#regex (light|dark)' }
})
Schema Reuse
Define schemas once in the Background section and reuse them across multiple scenarios. Use karate.merge()
to combine schemas for complex validations.
Advanced Schema Patterns
Dynamic Schemas
Feature: Dynamic schema validation
Scenario: Conditional schema based on response
Given url baseUrl
And path 'api/dynamic-data'
When method get
Then status 200
* def responseType = response.type
* def schema = responseType == 'user' ?
{ id: '#number', name: '#string', email: '#string' } :
responseType == 'product' ?
{ id: '#number', name: '#string', price: '#number' } :
{ id: '#number', type: '#string' }
* match response == schema
Scenario: Environment-specific schema
* def env = karate.env || 'dev'
Given url baseUrl
And path 'api/config'
When method get
Then status 200
* def baseSchema = { version: '#string', features: '#[] #string' }
* def envSchema = env == 'prod' ?
karate.merge(baseSchema, { debugInfo: '#notpresent' }) :
karate.merge(baseSchema, { debugInfo: '#object' })
* match response == envSchema
Nested Complex Schemas
Feature: Nested schema validation
Scenario: Multi-level nested structures
Given url baseUrl
And path 'api/user-profile'
When method get
Then status 200
And match response == {
user: {
id: '#number',
profile: {
personal: {
firstName: '#string',
lastName: '#string',
age: '#number? _ >= 18'
},
contact: {
email: '#regex .+@.+',
phone: '##regex \+?[0-9\-\s]+'
},
preferences: {
theme: '#regex (light|dark)',
notifications: {
email: '#boolean',
sms: '#boolean'
}
}
},
permissions: '#[] #string'
}
}
Cross-Field Validation
Feature: Cross-field validation
Scenario: Validate calculated fields
Given url baseUrl
And path 'api/financial-summary'
When method get
Then status 200
And match response == {
transactions: '#[]',
summary: {
count: '#? _ == $.transactions.length',
total: '#number',
average: '#? _ == $.total / $.count',
breakdown: {
income: '#number? _ >= 0',
expenses: '#number? _ >= 0',
net: '#? _ == $.income - $.expenses'
}
}
}
External Schema Files
Feature: External schemas
Scenario: Load schema from file
Given url baseUrl
And path 'api/users'
When method get
Then status 200
* def userSchema = read('schemas/user-schema.json')
And match each response.users == userSchema
Scenario: Combine external schemas
* def profileSchema = read('schemas/profile-schema.json')
* def addressSchema = read('schemas/address-schema.json')
* def completeSchema = karate.merge(profileSchema, {
address: addressSchema
})
Given url baseUrl
And path 'api/users', 123
When method get
Then status 200
And match response == completeSchema
Schema Inheritance
Feature: Schema inheritance
Scenario: Extend base schema
# Base schema from file
* def baseSchema = read('schemas/base-entity-schema.json')
* def productSchema = karate.merge(baseSchema, {
name: '#string',
price: '#number? _ > 0',
category: '#string',
inventory: {
inStock: '#boolean',
quantity: '#number? _ >= 0'
}
})
Given url baseUrl
And path 'api/products', 123
When method get
Then status 200
And match response == productSchema
Next Steps
Master schema validation and continue with:
- Fuzzy Matching - Deep dive into validation markers and edge cases
- Match Keyword - Review core match operations
- HTTP Responses - Apply schemas to API response validation