ADVANCED
Conditional Logic
Implement if statements, ternary operators, and decision trees to make your Karate tests adapt dynamically to API responses and runtime conditions.
On this page:
- If statements - Inline conditional execution
- Ternary operators - Concise conditional assignment
- karate.abort() - Exit without failing
- karate.fail() - Fail with custom message
- JSON transforms - map, filter, forEach operations
- Loops - karate.repeat() and karate.range()
Simple If Statements
Use inline if statements for basic conditional execution:
Feature: Basic if conditions
Scenario: Inline if statements
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200
* if (response.id == 1) karate.log('Found user 1')
* if (response.name == null) karate.fail('Name should not be null')
Tests that give different results on every run are discouraged. Use conditional logic sparingly for environment switching or handling known API variations.
Ternary Operators
Use ternary operators for concise conditional assignments:
Feature: Ternary operators
Scenario: Conditional assignment
* def env = karate.env || 'dev'
* def baseUrl = env == 'prod' ? 'https://jsonplaceholder.typicode.com' : 'https://httpbin.org'
* def timeout = env == 'prod' ? 10000 : 5000
* print 'Using: ', baseUrl, ' with timeout: ', timeout
Conditional Variable Assignment
Use ternary operators to set different values based on conditions:
Feature: Conditional assignment
Scenario: Select endpoint based on condition
* def useV2 = true
* def endpoint = useV2 ? 'users' : 'posts'
Given url 'https://jsonplaceholder.typicode.com'
And path endpoint, 1
When method get
Then status 200
* match response.username == 'Bret'
Conditional Response Handling
Handle different response scenarios with ternary operators:
Feature: Conditional response handling
Scenario: Handle response based on status
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200
* def userName = response.name != null ? response.name : 'Unknown'
* match userName == 'Leanne Graham'
Controlling Test Flow
Using karate.abort()
Exit a scenario without marking it as failed:
Feature: Abort scenario
Scenario: Skip test conditionally
* def env = karate.env || 'dev'
* if (env == 'local') karate.abort()
Given url 'https://jsonplaceholder.typicode.com'
And path 'users'
When method get
Then status 200
Use karate.abort() to skip tests in specific environments or when preconditions are not met.
Using karate.fail()
Explicitly fail a test with a custom message:
Feature: Fail with message
Scenario: Fail with descriptive message
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200
* if (response.id == null) karate.fail('User ID should not be null')
* if (response.name == '') karate.fail('User name should not be empty')
Always use descriptive error messages with karate.fail() to make debugging easier in test reports. Prefer match statements for simple assertions since they provide detailed error output.
Conditional Feature Calls with karate.call()
When you need to call a feature conditionally inside an if statement, use karate.call():
Feature: Conditional feature execution
Scenario: Call feature only when needed
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200
* if (responseStatus == 200) karate.call('process-user.feature')
The call read() syntax cannot be used inside if statements. Use karate.call() instead:
# WRONG - will cause syntax error
* if (condition) call read('feature.feature')
# CORRECT - use karate.call()
* if (condition) karate.call('feature.feature')
For shared scope with conditional logic, add true as the first argument:
* if (condition) karate.call(true, 'feature.feature')
Multi-line Conditional Blocks
For complex logic, use JavaScript function blocks:
Feature: Multi-line conditionals
Scenario: Complex conditional logic
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200
* def handleResponse =
"""
function(resp) {
if (resp.id > 0) {
karate.log('Valid user ID:', resp.id);
return true;
} else {
karate.log('Invalid user ID');
return false;
}
}
"""
* def isValid = handleResponse(response)
* match isValid == true
Environment-Based Logic
Adapt tests to different environments using karate.env:
Feature: Environment-aware testing
Scenario: Environment-specific configuration
* def env = karate.env || 'dev'
* def config =
"""
{
"prod": { "validateStrict": true },
"staging": { "validateStrict": false },
"dev": { "validateStrict": false }
}
"""
* def settings = config[env]
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200
* if (settings.validateStrict) karate.match(response.email, '#regex .+@.+')
JSON Lookup Pattern
Use JSON objects for data-driven conditional logic instead of switch statements:
Feature: JSON lookup
Scenario: Data-driven branching
* def data =
"""
{
"foo": "hello",
"bar": "world"
}
"""
* def key = 'bar'
* match (data[key]) == 'world'
JSON Transforms
Transform JSON data using functional-style operations. Karate supports ES6 arrow functions and native JavaScript array methods.
Map and Filter Operations
Feature: Map and filter
Scenario: Karate map operation
* def fun = function(x){ return x * x }
* def list = [1, 2, 3]
* def res = karate.map(list, fun)
* match res == [1, 4, 9]
Scenario: Karate filter operation
* def fun = function(x){ return x % 2 == 0 }
* def list = [1, 2, 3, 4]
* def res = karate.filter(list, fun)
* match res == [2, 4]
Scenario: Convert array to different shape
* def before = [{ foo: 1 }, { foo: 2 }, { foo: 3 }]
* def fun = function(x){ return { bar: x.foo } }
* def after = karate.map(before, fun)
* match after == [{ bar: 1 }, { bar: 2 }, { bar: 3 }]
Karate Transform Functions
Feature: Karate transforms
Scenario: Using karate transform functions
# karate.mapWithKey converts primitives to objects
* def list = ['Bob', 'Wild', 'Nyan']
* def data = karate.mapWithKey(list, 'name')
* match data == [{ name: 'Bob' }, { name: 'Wild' }, { name: 'Nyan' }]
Scenario: filterKeys example
* def schema = { a: '#string', b: '#number', c: '#boolean' }
* def response = { a: 'x', c: true }
* match response == karate.filterKeys(schema, response)
* match karate.filterKeys(response, 'b', 'c') == { c: true }
* match karate.filterKeys(response, ['a', 'b']) == { a: 'x' }
Scenario: merge example
* def foo = { a: 1 }
* def bar = karate.merge(foo, { b: 2 })
* match bar == { a: 1, b: 2 }
ForEach Operations
Feature: ForEach operations
Scenario: karate.forEach() works on object key-values
* def keys = []
* def vals = []
* def idxs = []
* def fun =
"""
function(x, y, i) {
karate.appendTo(keys, x);
karate.appendTo(vals, y);
karate.appendTo(idxs, i);
}
"""
* def map = { a: 2, b: 4, c: 6 }
* karate.forEach(map, fun)
* match keys == ['a', 'b', 'c']
* match vals == [2, 4, 6]
* match idxs == [0, 1, 2]
Loops and Data Generation
Generate test data using karate.repeat() and karate.range():
Feature: Data generation
Scenario: Generate data with karate.repeat
* def fun = function(i){ return i * 2 }
* def foo = karate.repeat(5, fun)
* match foo == [0, 2, 4, 6, 8]
Scenario: Generate test data
* def fun = function(i){ return { name: 'User ' + (i + 1) } }
* def foo = karate.repeat(3, fun)
* match foo == [{ name: 'User 1' }, { name: 'User 2' }, { name: 'User 3' }]
Scenario: Generate range of numbers
* def foo = karate.range(4, 9)
* match foo == [4, 5, 6, 7, 8, 9]
Use karate.repeat() instead of JavaScript for loops to keep code concise and readable.
Sorting and Deduplication
Feature: Sort and dedupe
Scenario: Sort by nested property
* def foo = [{a: { b: 3 }}, {a: { b: 1 }}, {a: { b: 2 }}]
* def fun = function(x){ return x.a.b }
* def bar = karate.sort(foo, fun)
* match bar == [{a: { b: 1 }}, {a: { b: 2 }}, {a: { b: 3 }}]
* match bar.reverse() == [{a: { b: 3 }}, {a: { b: 2 }}, {a: { b: 1 }}]
Scenario: Remove duplicates
* def numbers = [1, 2, 2, 3, 3, 3, 4]
* def unique = karate.distinct(numbers)
* match unique == [1, 2, 3, 4]
Switch-Case Pattern
Implement switch-case patterns with JavaScript functions:
Feature: Switch-case pattern
Scenario: Handle multiple cases
* def handleStatus =
"""
function(status) {
switch(status) {
case 'pending': return { action: 'wait' };
case 'processing': return { action: 'poll' };
case 'completed': return { action: 'validate' };
case 'failed': return { action: 'retry' };
default: return { action: 'unknown' };
}
}
"""
Given url 'https://jsonplaceholder.typicode.com'
And path 'posts', 1
When method get
Then status 200
* def decision = handleStatus('completed')
* match decision.action == 'validate'
Common Patterns Reference
| Pattern | Use Case | Example |
|---|---|---|
| Inline if | Single statement | * if (status == 404) karate.abort() |
| Ternary | Conditional assignment | * def url = env == 'prod' ? prodUrl : devUrl |
| JSON lookup | Data-driven branching | * def val = config[key] |
| karate.abort() | Skip without failing | * if (env == 'local') karate.abort() |
| karate.fail() | Fail with message | * if (!valid) karate.fail('Invalid data') |
| karate.call() | Conditional feature call | * if (cond) karate.call('feature.feature') |
Next Steps
- Learn about lifecycle hooks: Hooks and Lifecycle
- Debug complex conditional logic: Debugging
- Set environment-specific configurations: Configuration
- Conditionally execute features: Calling Features