Skip to main content

REUSABILITY

Dynamic Scenarios

What You'll Learn

  • Create dynamic Scenario Outlines with runtime data sources
  • Use @setup tag for data preparation and scenario generation
  • Implement conditional scenario execution based on environment
  • Generate test scenarios from API responses or databases
  • Filter and transform data for targeted test execution
  • Combine multiple data sources for complex test scenarios

Overview

Dynamic scenarios allow you to generate and execute test scenarios based on runtime conditions, external data sources, or API responses. This powerful capability enables adaptive testing that responds to changing environments, data-driven test generation, and complex test orchestration patterns.

Dynamic Scenario Outline

Single Cell Examples

When an Examples table has exactly one row and one column, Karate treats it as a dynamic data source:

Feature: Dynamic data-driven testing

Scenario Outline: Process item: ${name}
Given url apiUrl
And path 'items', id
And request { name: '#(name)', type: '#(type)' }
When method post
Then status 201

# Single cell triggers dynamic mode
Examples:
| read('classpath:test-data/items.json') |
```gherkin

### Runtime Data Generation

Generate test data at runtime using JavaScript functions:

```gherkin
Background:
* def generateData =
"""
function() {
var result = [];
for (var i = 1; i <= 10; i++) {
result.push({
id: i,
name: 'Item-' + i,
value: Math.floor(Math.random() * 100)
});
}
return result;
}
"""

Scenario Outline: Test generated item: ${name}
* print 'Testing item:', name, 'with value:', value
* match value == '#number'
* match value >= 0 && value < 100

Examples:
| generateData() |
```gherkin

## The @setup Tag

### Basic Setup Scenario

The `@setup` tag creates a special scenario that runs before the feature and prepares data:

```gherkin
Feature: Testing with setup data

@setup
Scenario: Prepare test data
* url apiUrl
* path '/test-data/generate'
* method get
* status 200
* def testItems = response.items
* def testConfig = response.config

Scenario Outline: Process ${name} with config
* print 'Config:', karate.setup().testConfig
* url apiUrl
* path 'items', id
* header X-Config = karate.setup().testConfig.token
* method get
* status 200

Examples:
| karate.setup().testItems |
```gherkin

### Setup with API Calls

Use `@setup` to fetch data from APIs before test execution:

```gherkin
@setup
Scenario: Get active users from API
* url userServiceUrl
* path '/users/active'
* method get
* status 200
* def activeUsers = response.users

# Filter for specific test scenarios
* def testUsers = activeUsers.filter(u => u.role == 'tester')

Scenario Outline: Validate user ${username}
* url apiUrl
* path '/validate', userId
* method post
* status 200
* match response.valid == true

Examples:
| karate.setup().testUsers |
```gherkin

### Named Setup Scenarios

Use named setup scenarios for multiple data preparations:

```gherkin
@setup=users
Scenario: Setup user data
* def userData = read('classpath:test-data/users.json')

@setup=products
Scenario: Setup product data
* def productData = read('classpath:test-data/products.json')

Scenario Outline: Test user ${name}
Examples:
| karate.setup('users').userData |

Scenario Outline: Test product ${productName}
Examples:
| karate.setup('products').productData |
```gherkin

## Setup Once Pattern

### Efficient Data Loading

Use `karate.setupOnce()` to run setup only once per feature:

```gherkin
@setup
Scenario: Expensive setup operation
* print 'This runs only once'
* url dataServiceUrl
* path '/generate-test-data'
* method post
* request { count: 100 }
* status 200
* def testData = response.data

Scenario Outline: First test batch: ${id}
* print 'Testing:', id

Examples:
| karate.setupOnce().testData[0:50] |

Scenario Outline: Second test batch: ${id}
* print 'Testing:', id

Examples:
| karate.setupOnce().testData[50:100] |
```gherkin

## Conditional Scenarios

### Environment-Based Execution

Generate scenarios based on environment:

```gherkin
@setup
Scenario: Environment-specific setup
* def env = karate.env
* def scenarios = []

* if (env == 'prod') scenarios = read('prod-scenarios.json')
* if (env == 'staging') scenarios = read('staging-scenarios.json')
* if (env == 'dev') scenarios = read('dev-scenarios.json')

# Add environment-specific headers
* def enrichedScenarios = scenarios.map(s => {
s.headers = { 'X-Environment': env };
return s;
})

Scenario Outline: ${testName}
* url apiUrl
* path endpoint
* headers headers
* method method
* status expectedStatus

Examples:
| karate.setup().enrichedScenarios |
```gherkin

### Feature Toggle Testing

Test different feature combinations:

```gherkin
@setup
Scenario: Get feature flags
* url configServiceUrl
* path '/features/active'
* method get
* status 200
* def activeFeatures = response.features

# Create test scenarios for each active feature
* def featureTests = activeFeatures.map(f => ({
featureName: f.name,
endpoint: f.testEndpoint,
expectedBehavior: f.expectedBehavior
}))

Scenario Outline: Test feature: ${featureName}
* url apiUrl
* path endpoint
* method get
* status 200
* match response contains expectedBehavior

Examples:
| karate.setup().featureTests |
```gherkin

## Data Filtering and Transformation

### Filtered Test Execution

Filter data for specific test scenarios:

```gherkin
@setup
Scenario: Setup with filtering
* def allData = read('classpath:large-dataset.json')

# Filter for specific test criteria
* def criticalTests = allData.filter(d => d.priority == 'critical')
* def edgeCases = allData.filter(d => d.type == 'edge-case')
* def happyPath = allData.filter(d => d.type == 'happy-path')

Scenario Outline: Critical test: ${name}
* print 'Running critical test:', name
# Test implementation

Examples:
| karate.setup().criticalTests |
```gherkin

### Data Transformation

Transform data before test execution:

```gherkin
@setup
Scenario: Transform test data
* def rawData = read('classpath:raw-test-data.json')

# Enrich data with additional fields
* def enrichedData = rawData.map(item => {
item.timestamp = new Date().toISOString();
item.testId = java.util.UUID.randomUUID().toString();
item.environment = karate.env;
return item;
})

Scenario Outline: Test ${testId}
* print 'Running test at:', timestamp
* url apiUrl
* path '/test', testId
* request payload
* method post
* status 201

Examples:
| karate.setup().enrichedData |
```gherkin

## Complex Data Sources

### Database-Driven Scenarios

Generate scenarios from database queries:

```gherkin
@setup
Scenario: Load from database
* def DbUtils = Java.type('com.example.DbUtils')
* def connection = DbUtils.getConnection()

# Query test scenarios from database
* def testCases = DbUtils.query(connection,
"SELECT * FROM test_cases WHERE status = 'pending' AND priority = 'high'")

# Transform database results
* def scenarios = testCases.map(tc => ({
id: tc.test_id,
name: tc.test_name,
input: JSON.parse(tc.input_json),
expected: JSON.parse(tc.expected_json)
}))

* eval DbUtils.closeConnection(connection)

Scenario Outline: Database test: ${name}
* url apiUrl
* path '/test', id
* request input
* method post
* status 200
* match response == expected

Examples:
| karate.setup().scenarios |
```gherkin

### Multi-Source Aggregation

Combine data from multiple sources:

```gherkin
@setup
Scenario: Aggregate multiple data sources
# Load from file
* def fileData = read('classpath:test-data.json')

# Call API
* url dataServiceUrl
* path '/current-data'
* method get
* status 200
* def apiData = response.data

# Generate dynamic data
* def dynamicData = []
* eval for(var i = 0; i < 5; i++) dynamicData.push({ id: 'gen-' + i })

# Combine all sources
* def allTestData = fileData.concat(apiData).concat(dynamicData)

Scenario Outline: Combined test: ${id}
* print 'Testing:', id
# Test implementation

Examples:
| karate.setup().allTestData |
```gherkin

## Advanced Patterns

### Recursive Test Generation

Generate tests that create more tests:

```gherkin
@setup
Scenario: Recursive test setup
* def generateTestTree =
"""
function(depth, breadth) {
var tests = [];
for (var i = 0; i < breadth; i++) {
var test = {
id: 'test-' + depth + '-' + i,
depth: depth
};
if (depth > 0) {
test.children = generateTestTree(depth - 1, breadth);
}
tests.push(test);
}
return tests;
}
"""
* def testTree = generateTestTree(3, 2)

Scenario Outline: Hierarchical test: ${id}
* print 'Testing at depth:', depth
# Test implementation

Examples:
| karate.setup().testTree |
```gherkin

### Conditional Data Loading

Load different data based on conditions:

```gherkin
@setup
Scenario: Conditional data loading
* def loadTestData =
"""
function() {
var hour = new Date().getHours();
var data;

if (hour < 12) {
// Morning tests
data = karate.read('classpath:morning-tests.json');
} else if (hour < 18) {
// Afternoon tests
data = karate.read('classpath:afternoon-tests.json');
} else {
// Evening tests
data = karate.read('classpath:evening-tests.json');
}

return data;
}
"""
* def testData = loadTestData()

Scenario Outline: Time-based test: ${name}
* print 'Running time-specific test:', name

Examples:
| karate.setup().testData |
```gherkin

## Best Practices

### Setup Organization

```gherkin
# ✅ Good: Clear, focused setup scenarios
@setup
Scenario: Prepare user test data
* def users = read('users.json')
* def validUsers = users.filter(u => u.valid)

# ❌ Avoid: Overly complex setup
@setup
Scenario: Do everything
# Too much logic in one setup
```gherkin

### Performance Considerations

```gherkin
# ✅ Good: Use setupOnce for expensive operations
@setup
Scenario: Expensive API call
* print 'This expensive call happens once'
* def data = karate.call('expensive-operation.feature')

Scenario Outline: Test ${id}
Examples:
| karate.setupOnce().data |
```gherkin

### Error Handling

```gherkin
# ✅ Good: Handle setup failures gracefully
@setup
Scenario: Safe setup
* def data = []
* try
* data = read('test-data.json')
* catch (e)
* print 'Failed to load data:', e
* data = [{ id: 'default', name: 'fallback' }]
```gherkin

## Troubleshooting

### Common Issues

```gherkin
# Issue: Setup not running
# Solution: Ensure @setup tag is present
@setup # Required tag
Scenario: Setup scenario

# Issue: Data not available
# Solution: Use karate.setup() to access
Examples:
| karate.setup().myData | # Correct
| myData | # Won't work

# Issue: Setup running multiple times
# Solution: Use setupOnce()
Examples:
| karate.setupOnce().data | # Runs once
```gherkin

## Next Steps

- Explore [Advanced Configuration](/docs/advanced/configuration) for environment setup
- Learn about [Hooks and Lifecycle](/docs/advanced/hooks) for test orchestration
- Understand [Parallel Execution](/docs/running-tests/parallel-execution) with dynamic scenarios