ADVANCED
Conditional Logic
What You'll Learn
- Implement if-else logic and conditional execution in tests
- Use abort and fail to control test flow
- Create dynamic test behavior based on runtime conditions
- Handle different environments and configurations conditionally
- Build utility functions for common conditional patterns
- Implement switch-case logic and complex decision trees
Overview
Conditional logic allows your tests to adapt to different scenarios, environments, and runtime conditions. Karate provides various mechanisms for implementing conditional behavior, from simple if-else statements to complex decision trees.
Basic Conditional Execution
If Statements
Execute code conditionally using if
statements:
Feature: Conditional execution
Scenario: Basic if conditions
* if (responseStatus == 404) karate.log('Resource not found')
* if (response.items.length == 0) karate.fail('No items returned')
* if (environment == 'prod') configure ssl = true
# Multi-line if blocks
* eval
"""
if (response.status == 'pending') {
karate.log('Status is pending, waiting...');
java.lang.Thread.sleep(2000);
karate.call('check-status.feature');
}
"""
```gherkin
### Ternary Operators
Use ternary operators for simple conditional assignments:
```gherkin
Scenario: Ternary operations
* def endpoint = environment == 'prod' ? '/api/v1' : '/api/test'
* def timeout = slowSystem ? 10000 : 5000
* def authToken = isAuthenticated ? token : null
# Nested ternary
* def priority = severity == 'critical' ? 'high' : severity == 'major' ? 'medium' : 'low'
```gherkin
## Abort and Fail
### Using karate.abort()
Exit a scenario without failing the test:
```gherkin
Scenario: Conditional abort
# Skip scenario for certain environments
* if (environment == 'local') karate.abort()
# Skip if precondition not met
* url apiUrl + '/check-ready'
* method get
* if (responseStatus != 200) karate.abort()
# Main test logic
* url apiUrl + '/main-test'
* method post
* status 200
```gherkin
### Using karate.fail()
Explicitly fail a test with custom message:
```gherkin
Scenario: Conditional failure
* url apiUrl + '/critical-operation'
* method post
* status 200
# Fail with descriptive message
* if (!response.success) karate.fail('Critical operation failed: ' + response.error)
# Multiple failure conditions
* if (response.errors && response.errors.length > 0) {
def errorMsg = 'Validation errors: ' + response.errors.join(', ')
karate.fail(errorMsg)
}
```gherkin
## Complex Conditional Flows
### Switch-Case Logic
Implement switch-case patterns using JavaScript:
```gherkin
Background:
* def handleStatus =
"""
function(status) {
switch(status) {
case 'pending':
return { action: 'wait', timeout: 5000 };
case 'processing':
return { action: 'poll', interval: 1000 };
case 'completed':
return { action: 'validate', checkData: true };
case 'failed':
return { action: 'retry', maxAttempts: 3 };
default:
return { action: 'error', message: 'Unknown status' };
}
}
"""
Scenario: Switch-case execution
* url apiUrl + '/status'
* method get
* def decision = handleStatus(response.status)
* if (decision.action == 'wait') java.lang.Thread.sleep(decision.timeout)
* if (decision.action == 'poll') call read('poll-status.feature') { interval: decision.interval }
* if (decision.action == 'retry') call read('retry-operation.feature') { attempts: decision.maxAttempts }
* if (decision.action == 'error') karate.fail(decision.message)
```gherkin
### Decision Trees
Build complex decision trees:
```gherkin
* def makeDecision =
"""
function(context) {
// Level 1: Check environment
if (context.env === 'prod') {
// Level 2: Check user type
if (context.userType === 'admin') {
return { test: 'full', validate: 'strict' };
} else if (context.userType === 'user') {
return { test: 'limited', validate: 'normal' };
}
} else if (context.env === 'staging') {
// Different logic for staging
if (context.feature === 'experimental') {
return { test: 'experimental', validate: 'loose' };
}
}
// Default
return { test: 'basic', validate: 'minimal' };
}
"""
Scenario: Complex decision making
* def context = { env: karate.env, userType: 'admin', feature: 'standard' }
* def decision = makeDecision(context)
* print 'Decision:', decision
* if (decision.test == 'full') call read('full-test-suite.feature')
* if (decision.validate == 'strict') call read('strict-validation.feature')
```gherkin
## Environment-Based Logic
### Environment-Specific Behavior
Adapt tests to different environments:
```gherkin
Feature: Environment-aware testing
Background:
* def env = karate.env || 'dev'
# Environment-specific configuration
* if (env == 'prod') configure ssl = true
* if (env == 'local') configure proxy = 'http://localhost:8888'
* if (env != 'prod') configure report = { showLog: true, showAllSteps: true }
Scenario: Environment-specific endpoints
* def baseUrl =
"""
{
'prod': 'https://api.production.com',
'staging': 'https://api.staging.com',
'dev': 'http://localhost:3000'
}
"""
* url baseUrl[env] + '/endpoint'
* method get
* status 200
# Environment-specific validation
* if (env == 'prod') match response.version == 'v2.0'
* if (env == 'dev') match response.debug == '#present'
```gherkin
### Feature Flags
Handle feature flags conditionally:
```gherkin
Background:
* url configUrl + '/features'
* method get
* status 200
* def features = response
Scenario: Feature flag testing
# Test new feature if enabled
* if (features.newCheckout.enabled) {
call read('test-new-checkout.feature')
} else {
call read('test-legacy-checkout.feature')
}
# Skip tests for disabled features
* if (!features.analytics.enabled) karate.abort()
# Different validation based on feature state
* def expectedFields = features.extendedProfile.enabled
? ['name', 'email', 'phone', 'address', 'preferences']
: ['name', 'email']
* match response.fields == expectedFields
```gherkin
## Data-Driven Conditions
### Conditional Data Processing
Process data conditionally:
```gherkin
Scenario: Process data based on type
* def processItem =
"""
function(item) {
if (item.type === 'user') {
item.processed = true;
item.role = item.role || 'default';
} else if (item.type === 'admin') {
item.processed = true;
item.permissions = ['read', 'write', 'delete'];
} else {
item.processed = false;
item.error = 'Unknown type';
}
return item;
}
"""
* def items = [
{ type: 'user', name: 'John' },
{ type: 'admin', name: 'Jane' },
{ type: 'unknown', name: 'Bob' }
]
* def processed = items.map(processItem)
* match processed[0].role == 'default'
* match processed[1].permissions == ['read', 'write', 'delete']
* match processed[2].error == 'Unknown type'
```gherkin
### Dynamic Validation
Apply different validation rules conditionally:
```gherkin
Scenario: Conditional validation
* def validateResponse =
"""
function(response, context) {
var errors = [];
// Always validate required fields
if (!response.id) errors.push('Missing id');
if (!response.status) errors.push('Missing status');
// Conditional validation based on status
if (response.status === 'completed') {
if (!response.completedAt) errors.push('Missing completedAt');
if (!response.result) errors.push('Missing result');
} else if (response.status === 'failed') {
if (!response.error) errors.push('Missing error');
if (!response.failedAt) errors.push('Missing failedAt');
}
// Environment-specific validation
if (context.env === 'prod' && !response.signature) {
errors.push('Missing signature in production');
}
return errors;
}
"""
* def errors = validateResponse(response, { env: karate.env })
* if (errors.length > 0) karate.fail('Validation errors: ' + errors.join(', '))
```gherkin
## Utility Functions
### Common Conditional Patterns
Create reusable conditional utilities:
```gherkin
Background:
# Retry with condition
* def retryIf =
"""
function(condition, action, maxAttempts) {
for (var i = 0; i < maxAttempts; i++) {
var result = action();
if (!condition(result)) {
return result;
}
if (i < maxAttempts - 1) {
java.lang.Thread.sleep(1000);
}
}
karate.fail('Max attempts reached');
}
"""
# Execute if not null
* def executeIfPresent =
"""
function(value, action) {
if (value !== null && value !== undefined) {
return action(value);
}
return null;
}
"""
# Choose first valid
* def firstValid =
"""
function() {
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] !== null && arguments[i] !== undefined) {
return arguments[i];
}
}
return null;
}
"""
Scenario: Use utility functions
# Retry if status is pending
* def checkStatus = function() {
return karate.call('get-status.feature').response
}
* def isPending = function(resp) { return resp.status === 'pending' }
* def result = retryIf(isPending, checkStatus, 5)
# Execute only if token exists
* def token = executeIfPresent(response.token, function(t) {
return 'Bearer ' + t;
})
# Use first available value
* def endpoint = firstValid(
karate.properties['custom.endpoint'],
java.lang.System.getenv('API_ENDPOINT'),
'https://default.api.com'
)
```gherkin
## Error Handling Conditions
### Try-Catch Patterns
Handle errors conditionally:
```gherkin
Scenario: Error handling
* def safeCall =
"""
function(feature, fallback) {
try {
return karate.call(feature);
} catch (e) {
karate.log('Error calling feature:', e.message);
if (fallback) {
return karate.call(fallback);
}
return { error: e.message };
}
}
"""
# Try primary, fallback to secondary
* def result = safeCall('primary-service.feature', 'fallback-service.feature')
# Handle based on success/failure
* if (result.error) {
print 'Using cached data due to error:', result.error
def data = read('classpath:cached-data.json')
} else {
def data = result.response
}
```gherkin
## Best Practices
### Clear Conditions
```gherkin
# ✅ Good: Clear, readable conditions
* if (response.status == 'active' && response.verified == true)
# ❌ Avoid: Complex nested conditions
* if ((a && b) || (c && !d) || (e && f && g))
# ✅ Better: Extract complex logic
* def shouldProcess = function() {
return (a && b) || (c && !d) || (e && f && g);
}
* if (shouldProcess())
```gherkin
### Meaningful Messages
```gherkin
# ✅ Good: Descriptive failure messages
* if (!response.data) karate.fail('No data returned from API')
# ❌ Avoid: Generic messages
* if (!response.data) karate.fail('Test failed')
```gherkin
## Next Steps
- Learn about [Hooks and Lifecycle](/docs/advanced/hooks) for test orchestration
- Explore [Debugging and Troubleshooting](/docs/advanced/debugging) techniques
- Understand [Test Organization](/docs/reusability/calling-features) patterns