REUSABILITY
Data-Driven Tests
Run the same test logic with multiple data sets using Scenario Outline or Karate's dynamic feature calling—no code duplication required.
Benefits of Data-Driven Testing
- Zero code duplication: Write test logic once, run with many variations
- Maintainable: Update test data separately from test logic
- Comprehensive coverage: Test edge cases without bloating test files
- Clear reporting: Each data set appears as separate test result
- Flexible data sources: Use inline tables, CSV, YAML, JSON, or external APIs
Simple Table Data
Create inline test data using the table
keyword to define JSON arrays directly in your tests.
Feature: Simple table data
Scenario: Create test data with table
* table users
| name | age |
| 'Alice' | 30 |
| 'Bob' | 25 |
| 'Carol' | 35 |
* match users == '#[3]'
* match users[0] == { name: 'Alice', age: 30 }
* match users[1].name == 'Bob'
String values must be quoted: 'Alice'
not Alice
. Numbers and booleans do not need quotes: 30
, true
, false
.
Basic Scenario Outline
Run the same scenario multiple times with different inputs using Scenario Outline and Examples tables.
Feature: Basic Scenario Outline
Scenario Outline: Validate user registration
Given url baseUrl
And path 'users'
And request { name: '<name>', email: '<email>' }
When method post
Then status <expectedStatus>
Examples:
| name | email | expectedStatus |
| Alice | alice@example.com | 201 |
| Bob | bob@example.com | 201 |
| | missing@test.com | 400 |
| Carol | invalid-email | 400 |
The Cucumber Way
Traditional Scenario Outline
Use Scenario Outline for fixed data sets with readable, self-documenting tables.
Feature: Cucumber-style data-driven tests
Scenario Outline: API validation with multiple inputs
Given url baseUrl
And path 'api/validate'
And request { username: '<username>', role: '<role>' }
When method post
Then status <status>
And match response.access == <hasAccess>
Examples:
| username | role | status | hasAccess |
| admin | admin | 200 | true |
| john | user | 200 | true |
| guest | guest | 403 | false |
| invalid | none | 401 | false |
Type Hints and Enhancements
Add !
suffix to column headers to evaluate values as JavaScript expressions instead of strings.
Feature: Type hints with ! suffix
Scenario Outline: Complex data types
* def testData = { count: <count>, active: <active>, tags: <tags> }
* match testData.count == '#number'
* match testData.active == '#boolean'
* match testData.tags == '#array'
Examples:
| count! | active! | tags! |
| 5 | true | ['a', 'b', 'c'] |
| 10 | false | ['x', 'y'] |
| 0 | true | [] |
The Karate Way
Dynamic Feature Calling
Call reusable features with array data for flexible, programmatic data-driven testing.
Feature: Karate-style dynamic testing
Scenario: Test multiple users dynamically
* def testUsers = [
{ name: 'Alice', role: 'admin', expectSuccess: true },
{ name: 'Bob', role: 'user', expectSuccess: true },
{ name: 'Guest', role: 'guest', expectSuccess: false }
]
* def results = call read('validate-access.feature') testUsers
* def successful = results.filter(r => r.success)
* match successful == '#[2]'
* print 'Tested', testUsers.length, 'users'
Arrays as Data Sources
Generate test data programmatically or load from variables for maximum flexibility.
Feature: Array-based data sources
Scenario: Generate dynamic test data
* def userCount = 5
* def generateUser = function(i) { return { id: i, name: 'User' + i } }
* def users = karate.repeat(userCount, generateUser)
* def results = call read('test-user.feature') users
* match results == '#[5]'
* match results[*].responseStatus == [201, 201, 201, 201, 201]
File-Based Data Sources
Load test data from external files to separate data from test logic.
CSV Data
Feature: CSV data sources
Scenario: Load users from CSV file
* def users = read('test-users.csv')
* match users == '#[]'
* print 'Loaded', users.length, 'users from CSV'
* def results = call read('create-user.feature') users
* match results[*].responseStatus contains only 201
YAML Data
Feature: YAML data sources
Scenario: Load structured test data
* def testData = read('test-scenarios.yaml')
* match testData.scenarios == '#[]'
* def results = call read('run-scenario.feature') testData.scenarios
* def failures = results.filter(r => r.failed)
* assert failures.length == 0
JSON Data
Feature: JSON data sources
Scenario Outline: Use JSON file in Examples
Given url baseUrl
And path 'api/process'
And request { type: '<type>', value: '<value>' }
When method post
Then status <expectedStatus>
Examples:
| read('test-cases.json') |
Advanced Scenario Outline
Magic Variables
Karate provides built-in variables __row
and __num
for accessing row data and index.
Feature: Magic variables
Scenario Outline: Access row data and index
* print 'Testing row', __num, 'with data:', __row
* match __row.name == '<name>'
* match __row.age == <age>
* match __num >= 0
Given url baseUrl
And path 'users'
And request __row
When method post
Then status 201
Examples:
| name | age! | email |
| Alice | 30 | alice@example.com |
| Bob | 25 | bob@example.com |
| Carol | 35 | carol@example.com |
Dynamic Scenario Names
Use placeholders in Scenario Outline names for better test reporting.
Feature: Dynamic scenario names
Scenario Outline: Validate <name> with role <role>
* print 'Testing user:', '<name>', 'role:', '<role>'
Given url baseUrl
And path 'access/check'
And request { username: '<name>', role: '<role>' }
When method post
Then status <expectedStatus>
Examples:
| name | role | expectedStatus |
| admin | admin | 200 |
| john | user | 200 |
| guest | guest | 403 |
Dynamic Data Generation
Programmatic Test Data
Generate test data with JavaScript functions for complex scenarios.
Feature: Programmatic data generation
Scenario: Generate test combinations
* def roles = ['admin', 'user', 'guest']
* def regions = ['US', 'EU', 'APAC']
* def testCases = []
* karate.forEach(roles, function(role) { karate.forEach(regions, function(region) { testCases.push({ role: role, region: region }) }) })
* match testCases == '#[9]'
* def results = call read('test-access.feature') testCases
* print 'Tested', testCases.length, 'combinations'
API-Driven Data
Load test data from external APIs for testing against live data sets.
Feature: API-driven test data
Scenario: Use API response as test data
Given url baseUrl
And path 'admin/active-users'
And header Authorization = 'Bearer ' + adminToken
When method get
Then status 200
* def users = response.users
* print 'Testing', users.length, 'active users'
* def results = call read('validate-user.feature') users
* def failures = results.filter(r => r.status != 200)
* assert failures.length == 0
Common Patterns
Testing Multiple Environments
Test the same logic across different environments efficiently.
Feature: Multi-environment testing
Scenario: Health check across all environments
* def environments = ['dev', 'qa', 'staging']
* def buildConfig = function(env) { return { env: env, url: karate.get('config.' + env + '.url') } }
* def configs = environments.map(buildConfig)
* def results = call read('health-check.feature') configs
* match results[*].responseStatus == [200, 200, 200]
* print 'All environments healthy'
Boundary Testing
Test edge cases and boundaries systematically with data tables.
Feature: Boundary value testing
Scenario Outline: Test age validation boundaries
Given url baseUrl
And path 'users/validate'
And request { name: 'Test', age: <age> }
When method post
Then status <expectedStatus>
Examples:
| age! | expectedStatus | description |
| -1 | 400 | # Negative |
| 0 | 400 | # Zero |
| 17 | 400 | # Under minimum |
| 18 | 200 | # Minimum valid |
| 120 | 200 | # Maximum valid |
| 121 | 400 | # Over maximum |
When to Use Each Approach
Choose the right data-driven approach based on your testing needs:
Scenario | Use This | Why |
---|---|---|
Fixed test cases (5-20 rows) | Scenario Outline | More readable, self-documenting tables |
Large data sets (100+ rows) | Karate Way | Better performance and memory usage |
Dynamic conditions | Karate Way | Can filter, transform, generate data |
Simple validation matrix | Scenario Outline | Tables are familiar to stakeholders |
Cross-environment testing | Karate Way | Can load environment-specific data |
Boundary testing | Scenario Outline | Clear visibility of edge cases |
For 100+ test cases, use Karate Way with call read()
instead of Scenario Outline. Better performance and avoids memory issues with large Examples tables.
Next Steps
- Master feature calling fundamentals: Calling Features
- Generate data programmatically: Dynamic Scenarios
- Validate complex responses: Schema Validation
- Run tests efficiently: Parallel Execution
- Learn about data types: Data Types