Scripting and Testing

Running Tests with Data

Running Tests with Data

Data-driven testing means running the same set of API requests many times — once for each row in a data file. Each row supplies different input values: different user credentials, different IDs, different expected outcomes.

Instead of creating a separate collection for each test scenario, you create one collection and supply a data file with all the variations. The Collection Runner handles the rest.


When to Use It

Data-driven testing is the right approach when:

  • You want to test the same endpoint with many different valid inputs — different user roles, different countries, different currencies.
  • You want to verify error cases alongside happy paths — valid credentials, invalid credentials, missing fields, all in one run.
  • You need to run a parametrised test suite — for example, creating 50 test users and verifying each one was created correctly.
  • You want your API tests to read like a specification — a file that states "given this input, expect this output."

Preparing Your Data File

The Collection Runner accepts two data file formats: CSV and JSON.

CSV Format

A CSV (comma-separated values) file. The first row is the header — the column names become variable names you can use in your requests.

username,password,expected_status,expected_role
alice@example.com,correct-password,200,admin
bob@example.com,correct-password,200,editor
carol@example.com,wrong-password,401,
,missing-username,400,
admin@example.com,admin-pass,200,admin

CSV formatting rules:

  • The header row is required — it defines the variable names.
  • One row = one iteration. The collection runs once per data row.
  • Values that contain commas must be wrapped in double quotes: "Smith, John".
  • Double quotes inside a value are escaped by doubling them: "She said ""hello""".
  • Empty values (like the role column in the 401 row above) are treated as empty strings.
  • If a row has more or fewer columns than the header, it is included but a warning is shown in the Preview — extra columns are ignored, missing columns are empty strings.
  • The file must be UTF-8 encoded. Excel-exported CSV files (which sometimes use UTF-8 BOM) are handled correctly.

JSON Format

A JSON file containing an array of objects. Each object is one iteration.

[
  { "userId": "101", "role": "admin",  "expectedName": "Alice Admin" },
  { "userId": "202", "role": "editor", "expectedName": "Bob Editor" },
  { "userId": "303", "role": "viewer", "expectedName": "Carol Viewer" }
]

JSON formatting rules:

  • The root must be an array ([...]), not an object.
  • Each item must be a plain object. Arrays or primitives at the root of an item are skipped with a warning.
  • All values are treated as strings internally. Numbers and booleans are converted with .toString().
  • Nested objects are JSON-stringified into a single string value — they are not expanded into dot-notation variables.
  • Missing keys in some objects are filled with empty strings.

Loading a Data File into the Runner

  1. Open the Collection Runner (click the Runner icon in the sidebar rail).
  2. Select your collection and environment.
  3. In the Data File section, click Upload CSV or JSON.
  4. Select your file.
  5. The runner shows how many rows (iterations) were found.
  6. Click Preview to inspect the parsed data before running.

Previewing Your Data

The Preview shows a paginated table of all rows. This is worth checking before a long run:

  • Rows with formatting problems are highlighted in yellow with an explanation ("Expected 4 columns, got 3").
  • You can page through the data to confirm it parsed as expected.
  • If something looks wrong, click Clear to remove the file, fix the source file, and re-upload.

Using Data Values in Requests

Once a data file is loaded, every column header becomes a variable that you can use anywhere in your requests — no script required for substitution.

In the URL

https://{{baseUrl}}/users/{{userId}}

For each iteration, {{userId}} is replaced with the value from that row's userId column.

In Headers

KeyValue
X-Role{{role}}
Content-Typeapplication/json

In the Request Body

{
  "username": "{{username}}",
  "password": "{{password}}"
}

In Auth Fields

Paste {{apiKey}} into an API Key auth value field and each iteration will use the key from that row.


Using Data Values in Test Scripts

Read data file values in scripts using fc.iterationData:

// Get a single column value
var expectedStatus = parseInt(fc.iterationData.get("expected_status"));
var expectedRole   = fc.iterationData.get("expected_role");
var username       = fc.iterationData.get("username");

// Get the entire current row as an object
var allValues = fc.iterationData.toObject();
console.log("Current row:", JSON.stringify(allValues));

Asserting Against a Data File Value

This is how you make each iteration's expected outcome specific to that row:

fc.test("Returns the expected status code", function () {
    var expected = parseInt(fc.iterationData.get("expected_status"));
    fc.expect(fc.response.code).to.equal(expected);
});

fc.test("Returns the expected role", function () {
    var expectedRole = fc.iterationData.get("expected_role");
    if (!expectedRole) return; // skip if no expected role for this row

    var body = fc.response.json();
    fc.expect(body.role).to.equal(expectedRole);
});

Using Data to Set Up Requests Dynamically

In a pre-request script, read data values to configure the request:

// Point to the right environment based on a data column
var env = fc.iterationData.get("environment");
fc.environment.set("baseUrl",
    env === "prod" ? "https://api.example.com" : "https://staging.api.example.com"
);

A Complete Example — Testing a Login Endpoint

Here is a full walkthrough using a login endpoint.

The Data File (login-tests.csv)

username,password,expected_status,description
alice@example.com,correct-pass,200,Valid login
bob@example.com,wrong-pass,401,Wrong password
,any-password,400,Missing username
alice@example.com,,400,Missing password
notauser@example.com,any-pass,401,Unknown user

The Request

  • Method: POST
  • URL: https://{{baseUrl}}/auth/login
  • Body (JSON):
{
  "username": "{{username}}",
  "password": "{{password}}"
}

The Test Script

var expectedStatus = parseInt(fc.iterationData.get("expected_status"));
var description    = fc.iterationData.get("description");

fc.test(description + " — status is " + expectedStatus, function () {
    fc.expect(fc.response.code).to.equal(expectedStatus);
});

// Only check for a token when we expect success
if (expectedStatus === 200) {
    fc.test(description + " — token is present", function () {
        var body = fc.response.json();
        fc.expect(body.access_token).to.exist;
        fc.expect(body.access_token).to.be.a("string");

        // Store for downstream requests
        fc.environment.set("authToken", body.access_token);
    });
}

// Only check the error message when we expect failure
if (expectedStatus >= 400) {
    fc.test(description + " — error message is present", function () {
        var body = fc.response.json();
        fc.expect(body.message).to.exist;
    });
}

Running It

  1. In the Collection Runner, upload login-tests.csv.
  2. The Preview shows 5 rows.
  3. Click Run (1).
  4. The runner executes the request 5 times — once per row — each with different username, password, and expected outcome values.
  5. Results are grouped under Iteration 1 through Iteration 5, showing which rows passed and which failed.

How Iterations Work

  • The entire request sequence runs once per data row.
  • Results are grouped in the results panel by iteration: Iteration 1, Iteration 2, etc.
  • Variables carry forward between iterations. If iteration 1 sets {{authToken}}, it is still set at the start of iteration 2. This is useful for sequences where earlier iterations provide data for later ones — but be aware of it if you need a clean state for each row.
  • If Stop on First Failure is enabled in run options, the entire run halts on the first failed assertion, regardless of which iteration it is in.

Viewing Results by Iteration

After the run, click any iteration in the results list to see the full detail for that iteration:

  • Which requests passed and which failed
  • The response body, headers, and timing for each request
  • Test assertion results with error messages for failures
  • Console log output from pre-request and test scripts

Exporting Results

Export the results of a data-driven run in the same formats as any other run:

  • PDF — a report showing every iteration and every request result
  • HTML — an interactive version you can open in a browser
  • JUnit XML — for CI systems like GitHub Actions, Jenkins, and GitLab CI

The export includes the iteration count and the data file name, so the report is self-documenting.


Practical Tips

Test error cases alongside happy paths in the same file A good data file tests both valid and invalid inputs. Include rows with missing fields, wrong types, boundary values, and maximum-length strings alongside normal inputs. One run covers the whole spec.

Name your test cases in the data Add a description or test_name column to your data file and include it in the fc.test() name. The results panel becomes much more readable:

var name = fc.iterationData.get("test_name");
fc.test(name, function () {
    var expected = parseInt(fc.iterationData.get("expected_status"));
    fc.expect(fc.response.code).to.equal(expected);
});

Keep data files alongside collection exports Store your CSV and JSON data files in the same folder as your exported collection. When you share or check in the collection, the data travels with it.

Preview before running large files Always click Preview before a run with hundreds of rows. A formatting error caught in Preview saves the time of running 300 iterations only to find the data was wrong.

Use JSON for complex data, CSV for simple tables CSV is easy to edit in Excel or Google Sheets and is ideal for flat, tabular data. JSON is better when values contain commas, special characters, or when you want to be explicit about types.