REUSABILITY
Dynamic Scenarios
Generate test scenarios dynamically at runtime using JSON arrays, CSV files, or custom generator functions for flexible, data-driven testing.
Why Dynamic Scenarios?
- Runtime flexibility: Resolve test data at execution time from files, databases, or APIs
- Scalable generation: Create hundreds or thousands of test cases without memory overhead
- External integration: Pull realistic test data from external sources like databases or web services
Simple JSON Data Source
Load test data from a JSON file at runtime:
Feature: Dynamic data from JSON
Scenario Outline: Create user: <name>
Given url baseUrl
And path 'users'
And request { name: '<name>', role: '<role>' }
When method post
Then status 201
And match response.name == '<name>'
# Single-cell table triggers dynamic resolution
Examples:
| read('test-users.json') |
Single-Cell Pattern
Dynamic Scenario Outline requires exactly one row and one column in the Examples table. This signals Karate to resolve the data at runtime instead of using a static table.
CSV File Data Source
Use CSV files for tabular test data:
Feature: CSV-driven tests
Scenario Outline: Test product: <name>
Given url baseUrl
And path 'products'
And request { name: '<name>', price: <price>, category: '<category>' }
When method post
Then status <expectedStatus>
Examples:
| read('product-test-data.csv') |
@setup Tag for Data Generation
Generate test data programmatically before running scenarios:
Feature: Setup tag for dynamic data
@setup
Scenario: Generate test data
* def testUsers = []
* def userTypes = ['admin', 'user', 'guest']
# Generate user test cases
* karate.forEach(userTypes, function(type) {
for (var i = 1; i <= 3; i++) {
testUsers.push({
name: type + 'User' + i,
type: type,
expectedStatus: type == 'guest' ? 403 : 201
});
}
})
Scenario Outline: Test user: <name>
Given url baseUrl
And path 'users'
And request { name: '<name>', type: '<type>' }
When method post
Then status <expectedStatus>
Examples:
| karate.setup().testUsers |
@setup Behavior
- @setup scenarios run before Background and all other scenarios
- Cannot depend on Background variables
- Access setup data using
karate.setup()
in the Examples table
Generator Functions
Simple Generator
Create large datasets without loading all data into memory:
Feature: Memory-efficient generator
@setup
Scenario: Setup generator function
# Generator function - returns null to signal end
* def userGenerator = function(index) {
if (index >= 50) return null; # Stop after 50 users
return {
name: 'User' + index,
email: 'user' + index + '@test.com',
active: index % 5 != 0 # 80% active
};
}
Scenario Outline: Test user: <name>
Given url baseUrl
And path 'users'
And request { name: '<name>', email: '<email>', active: <active> }
When method post
Then status 201
Examples:
| karate.setup().userGenerator |
Advanced Generator with Logic
Feature: Advanced generator patterns
@setup
Scenario: Complex generator
* def generator = function(index) {
if (index >= 100) return null;
return {
name: 'TestUser' + index,
department: ['engineering', 'sales', 'marketing'][index % 3],
expectedStatus: index % 10 == 0 ? 400 : 201 # 10% fail
};
}
Scenario Outline: User test <index> - <name>
Given url baseUrl
And path 'users'
And request { name: '<name>', department: '<department>' }
When method post
Then status <expectedStatus>
Examples:
| karate.setup().generator |
Advanced Patterns
Multiple Setup Scenarios
Combine multiple setup scenarios for complex test matrices:
Feature: Multiple setup scenarios
@setup @users
Scenario: Setup user data
* def users = [
{ id: 1, name: 'Admin', role: 'admin' },
{ id: 2, name: 'User', role: 'user' }
]
@setup @products
Scenario: Setup product data
* def products = [
{ id: 101, name: 'Laptop', price: 999 },
{ id: 102, name: 'Phone', price: 599 }
]
@setup @testMatrix
Scenario: Create test matrix
* def testMatrix = []
* karate.forEach(karate.setup('users').users, function(user) {
karate.forEach(karate.setup('products').products, function(product) {
testMatrix.push({
userId: user.id,
userName: user.name,
productId: product.id,
productName: product.name,
expectedAccess: user.role == 'admin' || product.price < 700
});
});
})
Scenario Outline: Access test - <userName> to <productName>
Given url baseUrl
And path 'access/product'
And request { userId: <userId>, productId: <productId> }
When method post
Then status 200
Examples:
| karate.setup('testMatrix').testMatrix |
Database-Driven Tests
Load test data from external databases:
Feature: Database-driven tests
@setup
Scenario: Load from database
* def DbUtils = Java.type('com.mycompany.DbUtils')
* def dbConfig = { url: 'jdbc:h2:mem:test', user: 'sa' }
# Query active test users
* def query = 'SELECT user_id, username, role FROM test_users WHERE active = true'
* def testUsers = DbUtils.readRows(dbConfig, query)
Scenario Outline: Database user test - <username>
Given url baseUrl
And path 'user/profile'
And request { userId: <user_id> }
When method get
Then status 200
And match response.role == '<role>'
Examples:
| karate.setup().testUsers |
Environment-Specific Generation
Generate different test cases based on environment:
Feature: Environment-specific tests
@setup
Scenario: Generate for environment
* def env = karate.env || 'dev'
* def testCases = []
# Base test cases for all environments
* def baseCases = [
{ endpoint: 'health', expectedStatus: 200 },
{ endpoint: 'version', expectedStatus: 200 }
]
* testCases = testCases.concat(baseCases)
# Add dev-only test cases
* if (env == 'dev') karate.set('testCases', testCases.concat([
{ endpoint: 'debug/info', expectedStatus: 200 }
]))
Scenario Outline: Test endpoint: <endpoint>
Given url baseUrl
And path '<endpoint>'
When method get
Then status <expectedStatus>
Examples:
| karate.setup().testCases |
When to Use Dynamic Scenarios
Use Dynamic Scenario Outline when you need:
- Test data determined at runtime (API responses, database queries, file generation)
- Large datasets (100+ test cases) without memory overhead using generator functions
- Environment-specific test generation based on configuration
Use static Examples tables when:
- Fixed, small dataset (fewer than 20 rows)
- Test cases known at design time and rarely change
- Simple data-driven testing without external dependencies
Next Steps
- Compare data-driven approaches: Data-Driven Tests
- Reuse test logic across features: Calling Features
- Add conditional test logic: Conditional Logic
- Explore parallel test execution: Parallel Execution