REUSABILITY
Data-Driven Tests
Run the same test logic with multiple data sets using Scenario Outline or dynamic feature calling. Write test logic once, execute with many variations.
On this page:
- table - Create JSON arrays from readable tables
- Scenario Outline - Cucumber-style data-driven tests
- Type hints (!) - Evaluate values as JavaScript types
- Magic variables - Access
__rowand__num - Dynamic calling - Call features with arrays
- External data - Load from CSV, JSON, YAML
Table Keyword
Create JSON arrays using the table keyword for readable inline data:
Feature: Table keyword
Scenario: Create test data with table
* table users
| name | age |
| 'Alice' | 30 |
| 'Bob' | 25 |
| 'Carol' | 35 |
* match users == [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }, { name: 'Carol', age: 35 }]
* match users[0] == { name: 'Alice', age: 30 }
String values require quotes: 'Alice'. Numbers and booleans don't: 30, true.
Scenario Outline
Run the same scenario multiple times with different inputs using Scenario Outline and Examples:
Feature: Scenario Outline
Scenario Outline: Get user by ID
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', <id>
When method get
Then status 200
And match response.name == '<expectedName>'
Examples:
| id | expectedName |
| 1 | Leanne Graham |
| 2 | Ervin Howell |
| 3 | Clementine Bauch |
Each row in Examples runs as a separate test. Placeholders like <id> are replaced with values from the table.
Type Hints
Add ! suffix to column headers to evaluate values as JavaScript expressions instead of strings:
Feature: Type hints
Scenario Outline: Test with typed data
* def data = { count: <count>, active: <active>, tags: <tags> }
* match data.count == '#number'
* match data.active == '#boolean'
* match data.tags == '#array'
Examples:
| count! | active! | tags! |
| 5 | true | ['a', 'b', 'c'] |
| 10 | false | ['x', 'y'] |
| 0 | true | [] |
Without !, all values are treated as strings. With !, values are evaluated as their JavaScript type.
Magic Variables
Karate provides __row (entire row as JSON) and __num (row index, starting at 0):
Feature: Magic variables
Scenario Outline: Access row data directly
* karate.log('Row', __num, ':', __row)
* match __row.name == '<name>'
Given url 'https://jsonplaceholder.typicode.com'
And path 'posts'
And request __row
When method post
Then status 201
Examples:
| name | title! | userId! |
| Alice | 'First Post' | 1 |
| Bob | 'Second Post' | 2 |
Use __row when you want to pass the entire row as a request body or validate multiple fields at once.
Dynamic Scenario Names
Include placeholders in scenario names for better test reports:
Feature: Dynamic names
Scenario Outline: Validate user <name> with ID <id>
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', <id>
When method get
Then status 200
And match response.name == '<name>'
Examples:
| id | name |
| 1 | Leanne Graham |
| 2 | Ervin Howell |
Dynamic Feature Calling
Call a feature file with an array to run it once per element:
Feature: Dynamic calling
Scenario: Test multiple users
* def testUsers = [{ id: 1 }, { id: 2 }, { id: 3 }]
* def results = call read('get-user.feature') testUsers
* match results[*].responseStatus == [200, 200, 200]
The called feature (get-user.feature) receives each array element as variables:
Feature: Get user
Scenario: Get user by ID
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', id
When method get
Then status 200
External Data Sources
CSV Files
Load test data from CSV files:
Feature: CSV data
Scenario: Load users from CSV
* def users = read('classpath:test-data/users.csv')
* match users == '#[]'
* def results = call read('create-user.feature') users
JSON Files
Use JSON files directly in Examples:
Feature: JSON data
Scenario Outline: Test from JSON file
Given url 'https://jsonplaceholder.typicode.com'
And path 'posts'
And request { title: '<title>', body: '<body>', userId: <userId> }
When method post
Then status 201
Examples:
| read('classpath:test-data/posts.json') |
YAML Files
Load structured data from YAML:
Feature: YAML data
Scenario: Load test scenarios from YAML
* def testData = read('classpath:test-data/scenarios.yaml')
* def results = call read('run-scenario.feature') testData.scenarios
Generate Test Data
Use karate.repeat() to generate test data programmatically:
Feature: Generate data
Scenario: Create users dynamically
* def generateUser = function(i){ return { id: i + 1, name: 'User' + (i + 1) } }
* def users = karate.repeat(5, generateUser)
* match users == '#[5]'
* match users[0] == { id: 1, name: 'User1' }
Tagged Examples
Tag specific rows in Examples for selective execution:
Feature: Tagged examples
Scenario Outline: Region-specific tests
* match region == expected
@region=US
Examples:
| region | expected |
| 'US' | 'US' |
@region=EU
Examples:
| region | expected |
| 'EU' | 'EU' |
Run with tag selector: --tags @region=US to execute only US examples.
When to Use Each Approach
| Scenario | Approach | Why |
|---|---|---|
| Fixed test cases (5-20 rows) | Scenario Outline | Readable, self-documenting |
| Large data sets (100+ rows) | Dynamic calling | Better performance |
| Dynamic/filtered data | Dynamic calling | Can transform data |
| Boundary testing | Scenario Outline | Clear edge case visibility |
| Cross-environment testing | Dynamic calling | Load environment-specific data |
For 100+ test cases, use dynamic calling with call read() instead of Scenario Outline for better performance.
Next Steps
- Calling Features - Feature reuse fundamentals
- Dynamic Scenarios - Runtime scenario generation
- Schema Validation - Validate response structures