API Data-Driven Scenarios in Cucumber with REST Assured
What Are API Data-Driven Scenarios?
API data-driven scenarios are API test scenarios that execute the same API workflow multiple times using different sets of input data. Instead of writing a separate scenario for every username, password, status code, customer payload, search filter, or invalid field, the framework reuses one behavior flow and supplies different datasets. This keeps feature files shorter, reduces duplication, and makes API coverage easier to scale.
In simple terms, data-driven API testing separates test logic from test data. The scenario describes the behavior. The data controls which examples are executed. REST Assured sends the request for each dataset, and the validation layer checks the expected response. Cucumber supports this style through Scenario Outlines, Examples Tables, Data Tables, and Doc Strings. Enterprise frameworks may also load data from JSON files, CSV files, Excel sheets, databases, external APIs, or runtime generators.
Data-driven design is especially useful in API automation because APIs often need many input combinations. A login API must handle valid credentials, invalid passwords, locked accounts, inactive users, expired passwords, missing fields, malformed data, and unauthorized access. A payment API must handle approved cards, declined cards, expired cards, insufficient funds, invalid CVV, duplicate transaction IDs, and currency mismatches. Writing each variation as a completely separate scenario creates unnecessary repetition.
Why Use Data-Driven Testing?
Data-driven testing improves maintainability. If the logic of a login API test is the same but the input data changes, one Scenario Outline is usually better than many repeated scenarios. The team can review all login combinations in one examples table. If the step wording changes, it changes once. If another row is needed, the tester adds data instead of duplicating a whole scenario.
Without data-driven testing, feature files often become repetitive. A team may create one scenario for admin login, one for invalid password, one for locked account, one for inactive account, and one for expired password. If each scenario has nearly identical steps, the file becomes longer without adding clarity. With data-driven testing, the same scenario can execute once per row and still produce separate Cucumber results.
Data-driven testing also improves coverage. It becomes easier to add positive, negative, boundary, and edge data. Instead of deciding whether another scenario is worth the space, the team can add another row or another external data record. This encourages broader validation while keeping the framework organized.
Data-Driven Execution Flow
The data-driven execution flow begins with the feature file or external data source. Cucumber reads a Scenario Outline or step data. Parameters are replaced with actual values for the current execution. Step definitions receive those values as method arguments. The request builder creates a payload or request configuration. REST Assured sends the API request. The response validator checks the expected status, body, headers, schema, and business rules. Then the next dataset is executed.
Feature file
-> Read test data
-> Replace parameters
-> Execute API request
-> Validate response
-> Repeat for next dataset
This flow makes one scenario behave like many tests. In reports, each examples row usually appears as a separate execution. This is valuable because a failure can be traced to a specific dataset. A good examples table uses clear values and expected outcomes so the failed row is easy to understand.
Common Data Sources
API test data can come from many places. Scenario Outlines and Examples Tables are built into Cucumber and work well for small datasets. Data Tables are useful for compact structured payloads. Doc Strings are useful for JSON, XML, GraphQL, or multiline payloads. JSON files are helpful for reusable request bodies. CSV files are useful for large tabular datasets. Excel files are common in enterprise projects where business users maintain data. Databases can provide live or prepared records. External APIs can provide setup data in integrated environments.
Test Data
Scenario Outline
Examples Table
Data Tables
Doc Strings
JSON Files
CSV Files
Excel Files
Database
External APIs
Dynamic generators
The correct data source depends on readability and maintenance. Small examples belong in feature files. Large data belongs outside feature files. Sensitive data belongs in secure configuration, not Gherkin. Dynamic unique values can be generated during execution. Mature frameworks support multiple data styles and use each one where it fits best.
Scenario Outline
Scenario Outline is the simplest form of data-driven testing in Cucumber. It allows placeholders in steps and replaces them with values from the Examples table. Cucumber executes the scenario once for each row. This is ideal when the behavior is the same and only the data changes.
Scenario Outline: Login API
When the client logs in using "<username>" and "<password>"
Then the response status should be <status>
Examples:
| username | password | status |
| admin | admin123 | 200 |
| admin | wrong | 401 |
| invalid | admin123 | 401 |
This example validates three login combinations without repeating the scenario body. The examples table is small and readable. A reviewer can quickly understand the valid and invalid combinations. If another credential case is needed, a new row can be added.
Step Definitions for Scenario Outline
Step definitions receive Scenario Outline values as parameters. Cucumber maps quoted strings to Java String arguments and numeric placeholders to suitable types when the step expression supports them. The step definition should pass those values to an API client or request builder rather than building all REST Assured code inline.
@When("the client logs in using {string} and {string}")
public void login(String username, String password) {
loginApi.login(username, password);
}
Each execution receives different parameters. The first row may send admin and admin123. The second row may send admin and wrong. The third row may send invalid and admin123. The Java method stays the same while data changes. This is the basic power of data-driven scenarios.
Execution Example
One Scenario Outline produces multiple scenario executions. If the Examples table has three rows, Cucumber runs the scenario three times. If it has ten rows, Cucumber runs ten times. Each row should be treated as a separate validation case with its own result in the report.
admin / admin123 -> Scenario execution 1
admin / wrong -> Scenario execution 2
invalid / admin123 -> Scenario execution 3
This behavior is useful for login, validation, search filters, boundary values, and status transitions. However, do not overload one outline with unrelated behavior. If rows require different setup, different actions, and different expected outcomes, separate scenarios may be clearer.
Data Tables
Data Tables are useful for structured request data inside a scenario. They are commonly used when a payload has several fields and one scenario needs a compact way to express them. Data Tables can be converted into maps, lists, or custom objects in Java step definitions.
Scenario: Create Customer
When the client creates a customer
| firstName | John |
| lastName | Smith |
| email | john@test.com |
Then the response status should be 201
This format is easier to read than a long step with many parameters. It is best for small payloads where the fields are meaningful to the scenario. The feature file remains readable, and the Java layer can convert the table into a request object.
Step Definitions for Data Tables
A step definition can accept a Cucumber DataTable parameter and convert it into a Map. This is useful for key-value data where each row represents one field and value. The map can then be passed to a request builder or API client.
@When("the client creates a customer")
public void createCustomer(DataTable table) {
Map<String, String> data =
table.asMap(String.class, String.class);
customerApi.createCustomer(data);
}
For larger or repeated payloads, converting Data Tables directly in step definitions can become messy. In that case, move conversion logic to a transformer, mapper, or request builder. Step definitions should stay thin and readable.
Doc Strings
Doc Strings are useful for multiline request payloads such as JSON, XML, or GraphQL. They allow the feature file to show a formatted payload exactly as it will be sent or close to how it will be sent. This is helpful when the payload shape is central to the scenario.
Scenario: Create Customer
When the client sends
"""
{
"name": "John",
"city": "Chicago"
}
"""
Then the response status should be 201
The step definition receives the Doc String as a normal String.
@When("the client sends")
public void sendPayload(String payload) {
customerApi.createCustomer(payload);
}
Doc Strings are excellent for readable JSON examples, especially negative payloads. However, very large JSON bodies can make feature files hard to read. If the payload becomes too large, use external JSON files or request builders instead.
JSON Test Data
JSON files are a natural fit for API request bodies because most REST APIs use JSON. A framework can store reusable payloads under a test data folder and load them before sending the request. This keeps feature files clean and allows payloads to be maintained separately.
testdata
createCustomer.json
{
"name": "John",
"city": "Chicago"
}
External JSON files are useful for large request bodies, repeated payloads, and payloads that closely resemble production API contracts. They also make it easier to compare payload changes in code review. The feature file can say which business case is being tested while the Java layer loads the correct file.
CSV Test Data
CSV files are useful for large tabular datasets. A login API with hundreds of credential combinations or a validation API with many boundary values may be easier to manage in CSV than in a huge Examples table. CSV files are simple, readable, and easy to generate from many tools.
username,password,status
admin,admin123,200
admin,wrong,401
The framework reads the CSV, converts rows into Java objects or maps, and passes the data into REST Assured requests. CSV is best when the data is flat. For nested payloads, JSON or POJO builders are usually better.
Excel Test Data
Excel test data is common in enterprise projects because business users, manual testers, and QA leads may already maintain test data in spreadsheets. Excel can store multiple sheets, columns, descriptions, expected status codes, and business labels. Java frameworks usually read Excel through Apache POI or a similar library.
Excel
-> Apache POI
-> Java data object
-> REST Assured request
Excel should be used carefully. It can become difficult to version, review, and merge compared with JSON or CSV. It is useful when business-managed data is a real requirement, but it should not be chosen only because it is familiar. Keep Excel files clean, documented, and limited to data that truly belongs there.
Database Test Data
Some API tests depend on database records. The framework may read prepared customer data, product data, account states, or configuration values from a database. Database-driven data can be useful when the API depends on complex existing state that is difficult to build through public APIs.
Database
-> Read customer data
-> Build API request
-> Validate response
Database data should be handled with caution. Tests that depend on shared live records can become unstable if another team changes the data. When possible, create controlled data before execution and clean it afterward. Direct database access should not replace API behavior validation unless the project has a strong reason.
POJO Mapping
POJOs improve readability and type safety for request payloads. Instead of building request bodies as raw strings or maps everywhere, the framework can create Java objects that represent request models. REST Assured can serialize those objects into JSON when configured correctly.
UserRequest request =
new UserRequest("John", "QA");
given()
.body(request)
.when()
.post("/users");
POJOs are especially helpful when payloads are reused across many tests. They provide structure and make refactoring easier. Builders can make POJO creation even cleaner by providing valid default values and allowing tests to override only the fields that matter.
Dynamic Test Data
Some values should be generated during execution. Create operations often need unique email addresses, usernames, order numbers, transaction IDs, or customer references. Dynamic data prevents duplicate conflicts when tests run repeatedly in the same environment.
john1719481000@test.com
Dynamic values can be based on timestamps, UUIDs, counters, or test-run identifiers. The generated value should be stored in scenario context if later steps need it. For example, a generated email may be used to retrieve the created user or clean up test data after execution.
Random Data
Random data can help create unique values, but it should be controlled. UUIDs are useful for uniqueness. Random names, phone numbers, and order numbers can prevent conflicts. However, completely random data may create hard-to-debug failures if it accidentally violates business rules. Random values should still follow valid formats and constraints.
String email =
UUID.randomUUID() + "@test.com";
When failures occur, the framework should log the generated values safely so the issue can be reproduced. If a test creates random data but does not report what was created, debugging becomes difficult. Use randomization for uniqueness, not chaos.
Scenario Context
Scenario context stores generated or extracted values during a Cucumber scenario. For API workflows, this is essential. A create customer call may return a customer ID. The scenario can store that ID, then use it for update, retrieve, delete, or cleanup calls.
Create Customer
-> Extract customerId
-> Scenario Context
-> Update Customer
-> Delete Customer
context.setCustomerId(id);
Scenario context should be scoped to one scenario. Avoid global static storage for test data because it can break parallel execution. Each scenario should own its own data values. This keeps tests independent and safer in CI/CD.
Request Builder
Request builders centralize payload creation. Instead of every step definition manually building maps or JSON strings, a builder can create valid default request objects. Tests can override only the fields required for the current scenario. This reduces duplication and keeps negative tests focused.
Request Builder
-> Create User
-> Create Order
-> Create Payment
For example, a user builder can create a valid user by default. A missing-email scenario can call the builder and remove or nullify only the email field. This makes the test easier to understand because it emphasizes the field under test instead of repeating all unrelated fields.
Environment-Specific Data
Different environments often require different data. Development, QA, staging, UAT, and production-like environments may have different base URLs, credentials, seed records, account IDs, and feature flags. This data should be separated from test logic.
dev-config.json
qa-config.json
stage-config.json
Environment-specific configuration should be loaded at runtime. Feature files should not contain environment URLs or real credentials. This keeps the same test suite portable across environments and reduces risk. CI/CD pipelines can choose the correct configuration based on the target environment.
Positive and Negative Data
A complete API data-driven suite includes both positive and negative datasets. Positive data proves that valid input succeeds. Negative data proves that invalid input is rejected correctly. Boundary data proves that minimum and maximum limits behave as expected. Edge data proves unusual but important combinations.
For a login API, valid username and password may return 200. Invalid password may return 401. Locked account may return 423 or a business-specific error. Missing username may return 400. For a payment API, approved, declined, expired, and invalid card scenarios may all use the same workflow but different data and expected outcomes.
Boundary and Edge Data
Boundary data tests limits. If a field allows 50 characters, test 49, 50, and 51. If quantity must be between 1 and 99, test 0, 1, 99, and 100. API validation often fails at boundaries because developers implement rules slightly differently than requirements.
Edge data tests unusual but realistic conditions. Examples include special characters in names, Unicode text, very long descriptions, empty arrays, duplicate items, large order totals, leap-year dates, and mixed-case email addresses. These cases are often better managed through data-driven design because they can be added without rewriting scenario logic.
Data-Driven Flow in Cucumber
The Cucumber data-driven flow connects feature data to Java automation. A Scenario Outline reads Examples rows. A Data Table is converted into a map or object. A Doc String is passed as a string payload. External data is read by Java utilities. Step definitions pass the data to request builders and API clients. REST Assured sends the request and validators check the response.
Feature File
-> Scenario Outline
-> Examples
-> Step Definition
-> Request Builder
-> REST Assured
-> API
-> Validation
This flow works best when each layer has a clear responsibility. Feature files should remain readable. Step definitions should stay thin. Request builders should prepare data. API clients should send requests. Validators should assert responses. Context should store temporary values.
Managing Large Datasets
Very large datasets should not be placed inside feature files. Hundreds of rows in an Examples table make Gherkin hard to read, slow to review, and difficult to maintain. Large data belongs in external JSON, CSV, Excel, database, or generated sources. The feature file should describe the behavior and reference the data category or source clearly.
When using large external datasets, reports should still identify which row failed. Include a test case name, scenario label, row ID, or data identifier in the dataset. Without a clear row identity, failures become hard to diagnose. Good data-driven frameworks make failed data easy to trace.
Test Data Cleanup
Create operations often leave data behind. If a scenario creates users, orders, payments, or accounts, the framework should clean up when appropriate. Cleanup may happen through API calls, database scripts, teardown hooks, or environment reset jobs. The approach depends on system design and test environment rules.
Cleanup should not hide real defects. If delete cleanup fails, report it clearly. If created data must remain for audit reasons, mark it with a test-run identifier so it can be filtered later. Good cleanup keeps environments stable and prevents data-driven tests from polluting shared systems.
Common Mistakes
One common mistake is hardcoding test data directly in Java methods, such as calling login with fixed credentials inside the step definition. This makes tests less reusable and harder to expand. Another mistake is creating duplicate scenarios where only input values differ. Scenario Outline or external data is usually better.
Very large Examples tables are another problem. They make feature files unreadable. Mixing business logic with test data is also risky. Feature files should remain understandable. Large JSON payloads or datasets should move to external files when they become difficult to read. Finally, teams often forget cleanup. Data-driven create operations can quickly pollute environments if cleanup is ignored.
Best Practices
Use Scenario Outlines for small datasets. Use Data Tables for compact structured input. Use Doc Strings for readable JSON or XML payloads. Store large datasets in JSON, CSV, Excel, or databases. Use POJOs for request payloads when structure is stable. Generate dynamic data where uniqueness is required. Separate test data from test logic.
Reuse request builders and scenario context. Include positive, negative, boundary, edge, and dynamic datasets. Clean up created data after execution when appropriate. Keep examples tables readable. Give external data rows meaningful names or IDs. Avoid exposing sensitive data in feature files or source control.
Enterprise Framework Architecture
An enterprise Cucumber REST Assured framework uses data-driven design through several layers. The feature file or external data source supplies input. Step definitions receive the data. Request builders convert it into request models. REST Assured sends the request. Response validators check the result. Scenario context stores generated and extracted values. Reports show the outcome per dataset.
Feature File
-> Scenario Outline
-> Examples
-> Step Definition
-> Request Builder
-> REST Assured
-> API
-> Response Validator
-> Report
External data sources such as JSON, CSV, Excel, and databases can plug into the same flow. The goal is not to use every data source everywhere. The goal is to choose the right source for the right testing problem and keep the framework maintainable.
Data Source Comparison
| Data Source | Best For | Recommended |
|---|---|---|
| Scenario Outline | Small datasets | Yes |
| Data Tables | Small structured payloads | Yes |
| Doc Strings | JSON/XML payloads | Yes |
| JSON Files | Large request bodies | Yes |
| CSV Files | Large tabular datasets | Yes |
| Excel Files | Business-managed test data | Yes |
| Database | Dynamic or prepared records | Yes |
| Random Data | Unique values | Yes |
This comparison is a practical guide, not a strict rule. A project may prefer JSON files over Excel because JSON is easier to version. Another project may use Excel because business teams maintain datasets. The best choice is the one that keeps tests clear, reliable, and maintainable.
Real-Time Example
Imagine a customer registration API. The team needs to validate successful registration, duplicate email rejection, missing first name, invalid email format, password below minimum length, phone number with invalid characters, and maximum address length. The workflow is the same: build a customer payload, send the request, and validate the response. The data changes for each rule.
A small set of validation cases can use a Scenario Outline. Larger request bodies can use JSON files named after each case. A request builder can create a valid customer by default and override only the field under test. Scenario context can store generated email addresses and created customer IDs. An After hook can clean up created customers. This design produces strong coverage without duplicating scenarios and step definitions.
Data Quality in API Automation
Data-driven testing is only useful when the data is trustworthy. Poor data creates misleading failures, false passes, and wasted debugging time. Every dataset should have a clear purpose. A row should exist because it validates a rule, boundary, permission, or important variation. If a row does not add new information, it may only slow execution and make reports noisy.
Good data includes readable names, expected outcomes, and enough context to understand failures. For example, a column named caseName or rule can explain why a row exists. "missing-email", "invalid-password-length", and "duplicate-username" are easier to understand in reports than anonymous row numbers. Clear data naming becomes very important when a large suite fails in CI.
Keeping Feature Files Readable
Feature files should remain readable even when tests are data-driven. A Scenario Outline with five rows and four columns is usually easy to review. A Scenario Outline with one hundred rows and fifteen columns is not. When data becomes large, the feature file stops documenting behavior and turns into a storage file. At that point, move the data outside the feature file.
A good feature file explains the behavior and shows representative examples. External data can hold broader coverage. This balance keeps Cucumber valuable as living documentation while still allowing the automation framework to execute many combinations. The feature file should help a person understand the test, not force them to scan a large spreadsheet inside Gherkin.
Data-Driven Reporting
Reporting is a major reason to design data-driven scenarios carefully. When a row fails, the report should show which data caused the failure. If the report only says that "Login API" failed, the team must inspect logs to find the exact username, account state, or expected status. If the examples table includes a clear case name, the failed row becomes much easier to diagnose.
For external data, include a row ID, scenario label, or business case name. The framework can attach that label to logs or reports. This is useful for Excel, CSV, JSON, and database-driven data. Data-driven reporting should answer three questions quickly: which dataset ran, what request was sent, and what response failed validation.
Parallel Execution Considerations
Data-driven API tests often run well in parallel because API calls are faster than UI flows. However, parallel execution requires isolated data. If multiple rows use the same username, order number, or customer record, they may interfere with each other. One row may update or delete data while another row is still using it. This creates flaky tests.
Use unique dynamic values for create operations, scenario-scoped context for extracted values, and independent setup for each row. Avoid shared mutable data unless it is read-only and stable. If test data must be shared, protect it carefully and avoid destructive operations. Parallel-ready data design is one of the signs of a mature API automation framework.
Data Security and Privacy
API datasets may contain credentials, tokens, customer information, account numbers, emails, phone numbers, addresses, or payment-like data. Test automation should avoid real personal or confidential information whenever possible. Use synthetic test data. Store secrets in secure configuration or CI secret stores. Do not commit passwords, API keys, or bearer tokens to feature files or test data files.
Reports and logs should also be reviewed. A failing data-driven test may print the request body or headers. If that data contains secrets or personal information, the report becomes a security risk. Mask sensitive fields before logging. This applies to Authorization headers, cookies, API keys, passwords, security answers, and any regulated test data.
Maintaining Data Over Time
As APIs evolve, test data must evolve with them. New required fields may be added. Old fields may be deprecated. Validation rules may change. Status codes may become more specific. If datasets are not maintained, failures increase and trust in automation decreases. Data maintenance should be part of normal test maintenance, not an afterthought.
Review data periodically. Remove duplicate rows. Rename unclear cases. Move oversized examples to external files. Update expected outcomes when requirements change intentionally. Keep request builders aligned with the latest contract. A data-driven suite can scale well only when the data is actively curated.
Choosing the Right Level of Data Coverage
More data is not always better. A login API does not need thousands of rows if most rows validate the same rule. A payment API may need carefully selected combinations rather than every possible card, currency, country, and status combination. Effective data-driven testing chooses meaningful coverage instead of uncontrolled volume.
Use risk to guide coverage. Critical business paths deserve positive, negative, boundary, role-based, and error datasets. Low-risk fields may need only representative cases. Pairwise or combinatorial techniques can help when many input fields interact. The goal is not maximum row count; the goal is useful confidence.
Using Tags with Data-Driven Scenarios
Tags help control data-driven execution. A Scenario Outline can be tagged as @Smoke, @Regression, @API, or module-specific. For larger datasets, tags can separate quick checks from full regression. A small smoke outline may run on every pull request, while a larger external dataset runs nightly.
Be careful not to use tags as test data. Tags should classify execution, module, priority, technology, or risk. They should not store usernames, environment URLs, or expected status codes. Data belongs in examples tables or external sources. Tags decide when scenarios run; data decides what input they run with.
Troubleshooting Data-Driven Failures
When a data-driven test fails, first identify the dataset. Check the row values, generated values, expected status, expected response body, and any setup data. Then inspect the request actually sent by REST Assured. Many failures come from incorrect data mapping, missing fields, wrong data types, stale external files, or environment-specific records.
Next, inspect the response. If the response is correct and the expected value is wrong, fix the test data. If the request is malformed, fix the mapper or builder. If the request is correct and the response violates the contract, raise a defect. This disciplined approach prevents random updates to data just to make tests pass.
Interview-Ready Summary
API data-driven scenarios allow the same API test logic to execute with multiple input datasets. Cucumber supports data-driven testing through Scenario Outlines, Examples Tables, Data Tables, and Doc Strings. Enterprise frameworks also use external data sources such as JSON, CSV, Excel, databases, and dynamically generated data. REST Assured works well with parameterized data, POJO request models, and request builders.
Separating test data from test logic improves maintainability, scalability, and reuse. A strong framework uses small examples in feature files, large datasets externally, dynamic data for uniqueness, scenario context for extracted values, and cleanup for created records. It includes positive, negative, boundary, and edge datasets.
Golden Rules
Separate test data from test logic. Use Scenario Outlines for repeated API behaviors with different inputs. Store large datasets in external files rather than feature files. Use POJOs and request builders for clean payload creation. Include positive, negative, boundary, edge, and dynamic test data in your API test suite.
The practical takeaway is direct: data-driven scenarios let one clean API workflow validate many meaningful data combinations. When the data is organized well and the framework layers are separated, Cucumber with REST Assured becomes much easier to scale.