DEV Community

Cover image for How to Run Parameterized API Tests From CSV and JSON
Hassann
Hassann

Posted on • Originally published at apidog.com

How to Run Parameterized API Tests From CSV and JSON

You wrote a login test. It passes. Then a teammate asks whether it also passes for a locked account, an unverified email, a password with a trailing space, or an SQL-injection string pasted into the field. You can copy the test five times, or you can run the same test against a table of inputs and expected outputs.

Try Apidog today

Copy-pasted tests are how suites rot. Five near-identical tests drift apart: one gets a new assertion, another misses a field rename, and soon you maintain five tests that should have been one. Parameterized testing fixes that by separating test logic from test data. You write the scenario once, then feed it many cases from CSV or JSON.

What parameterized testing means

Parameterized testing, also called data-driven testing, separates:

  • Test logic: send a request, check the status code, validate the response body.
  • Test data: inputs and expected outputs for each case.

For example, a discount-code endpoint always follows the same flow:

  1. Send POST /api/orders.
  2. Include a discount code and order total.
  3. Assert the status code.
  4. Assert the discount value.

Only the data changes:

code order_total expected_status expected_discount
WELCOME10 100 200 10
WELCOME10 5 422 0
EXPIRED 100 410 0
(empty) 100 400 0
FAKE123 100 404 0

That is five behaviors, but still one test. The runner iterates through each row, binds the row values to variables, sends the request, and checks the assertions.

When row 3 fails because EXPIRED returned 200 instead of 410, the report points to that iteration. You do not need to hunt through duplicated test files.

This pattern is most useful for edge cases:

  • Empty strings
  • Negative numbers
  • Unicode input
  • Expired tokens
  • Boundary values
  • Invalid IDs
  • Malformed payloads
  • Security-related strings

Adding one more case becomes as cheap as adding one more row.

Why external data files are better than hardcoded cases

You can hardcode cases inside a test, but that breaks down quickly.

External files give you a cleaner workflow:

  • QA can add cases without editing test logic.
  • Large case sets stay readable.
  • Generated test data can be stored as files.
  • Test logic stays small.
  • Expected results can vary per row.
  • Data changes are versioned with your repo.

Use the format that matches your data:

  • CSV for flat, tabular cases.
  • JSON for nested payloads or structured request bodies.

Apidog supports both CSV and JSON as iteration data, so the choice depends on the data shape.

Create a CSV data file

For tabular cases, start with CSV. The header row defines variable names. Each row is one test iteration.

Create discount-cases.csv:

code,order_total,expected_status,expected_discount
WELCOME10,100,200,10
WELCOME10,5,422,0
EXPIRED,100,410,0
,100,400,0
FAKE123,100,404,0
Enter fullscreen mode Exit fullscreen mode

Each column becomes a variable in the scenario:

  • {{code}}
  • {{order_total}}
  • {{expected_status}}
  • {{expected_discount}}

Use the input variables in the request body and the expected variables in assertions.

Example request body:

{
  "code": "{{code}}",
  "order_total": {{order_total}}
}
Enter fullscreen mode Exit fullscreen mode

Example assertions:

  • Response status equals {{expected_status}}
  • JSON field discount equals {{expected_discount}}

Use JSON for nested request data

If your payload contains nested objects or arrays, JSON is usually easier than CSV.

Create user-cases.json:

[
  {
    "scenario": "valid full profile",
    "user": {
      "email": "ada@example.com",
      "roles": ["admin", "billing"],
      "profile": {
        "country": "US",
        "timezone": "America/New_York"
      }
    },
    "expected_status": 201
  },
  {
    "scenario": "missing email",
    "user": {
      "email": "",
      "roles": ["viewer"],
      "profile": {
        "country": "GB",
        "timezone": "Europe/London"
      }
    },
    "expected_status": 400
  },
  {
    "scenario": "unknown role",
    "user": {
      "email": "grace@example.com",
      "roles": ["wizard"],
      "profile": {
        "country": "CA",
        "timezone": "America/Toronto"
      }
    },
    "expected_status": 422
  }
]
Enter fullscreen mode Exit fullscreen mode

In the scenario, reference the object with variables such as:

{
  "user": {{user}}
}
Enter fullscreen mode Exit fullscreen mode

Use {{expected_status}} in your status assertion.

The scenario field is useful as a label. In reports, a failed iteration can read unknown role instead of just iteration 3.

Keep data files maintainable

Use these rules when your test data grows:

  • Keep one concern per file.
    • Good: discount-cases.csv, user-creation-cases.json
    • Bad: one file with unrelated endpoint cases
  • Put expected results in the data file.
    • Different rows should be able to expect different statuses and response values.
  • Quote CSV values that contain commas.
    • If many values need escaping, switch to JSON.
  • Store data files in your repo.
    • Version them with the API and test configuration.
  • Do not store secrets in test data.
    • Use environment variables or CI secrets instead.

Build the parameterized scenario in Apidog

In Apidog, create the test scenario once.

  1. Add the request for your endpoint.
  2. Replace literal request values with variables:
    • {{code}}
    • {{order_total}}
  3. Add assertions that use expected values from the data file:
    • Status code equals {{expected_status}}
    • JSON response field equals {{expected_discount}}
  4. Open the scenario run settings.
  5. Attach the CSV or JSON file as iteration data.
  6. Run the scenario.

Apidog reads the file and runs the scenario once per row. Each run binds that row’s values to the matching variables.

If you need assertion examples, see API assertions: a practical guide and how to set assertions and extract variables from a JSON response.

A parameterized scenario is still a normal scenario, so you can group it into a test suite and run it with the rest of your API tests.

Run parameterized tests from the command line

Use the Apidog app to build and debug the scenario. Use the Apidog CLI to run it in CI.

Install the CLI:

npm install -g apidog-cli
Enter fullscreen mode Exit fullscreen mode

The binary is apidog. A basic run points to a scenario ID and environment ID:

apidog run --access-token $APIDOG_ACCESS_TOKEN -t 605067 -e 1629989 -r cli
Enter fullscreen mode Exit fullscreen mode

To run the scenario with a CSV or JSON data file, add -d:

apidog run --access-token $APIDOG_ACCESS_TOKEN -t 605067 -e 1629989 \
  -d ./discount-cases.csv -r cli,junit --out-dir ./test-reports
Enter fullscreen mode Exit fullscreen mode

The -d flag, also available as --iteration-data, is what turns a single scenario run into a data-driven run.

You can also pass a JSON file:

apidog run --access-token $APIDOG_ACCESS_TOKEN -t 605067 -e 1629989 \
  -d ./user-cases.json -r cli,junit --out-dir ./test-reports
Enter fullscreen mode Exit fullscreen mode

Keep the access token in your CI secret store. Do not commit it to your repo.

Useful CLI flags:

Flag Purpose
-d, --iteration-data <path> Use a CSV or JSON file as iteration data
-n, --iteration-count <n> Run a scenario a fixed number of times
-r, --reporters <list> Choose output formats such as cli and junit
--out-dir <path> Write reports to a directory

To check the current options for your installed version, run:

apidog run --help
Enter fullscreen mode Exit fullscreen mode

Run parameterized API tests in GitHub Actions

Here is a GitHub Actions workflow that installs the CLI and runs a CSV-driven scenario on every pull request:

name: API tests
on: [pull_request]

jobs:
  api-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install Apidog CLI
        run: npm install -g apidog-cli

      - name: Run parameterized API tests
        env:
          APIDOG_ACCESS_TOKEN: ${{ secrets.APIDOG_ACCESS_TOKEN }}
        run: |
          apidog run --access-token $APIDOG_ACCESS_TOKEN \
            -t 605067 -e 1629989 \
            -d ./tests/discount-cases.csv \
            -r cli,junit --out-dir ./test-reports

      - name: Upload report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: api-test-report
          path: ./test-reports
Enter fullscreen mode Exit fullscreen mode

This workflow does four things:

  1. Checks out the repo.
  2. Installs Node and the Apidog CLI.
  3. Runs the parameterized scenario with -d.
  4. Uploads reports even when the test fails.

The junit reporter writes XML that CI dashboards can parse into individual test results. That makes failed iterations easier to inspect than raw logs.

For more detail, see how to automate API tests in GitHub Actions.

The same pattern works in GitLab CI, Jenkins, CircleCI, and other CI systems:

  1. Install Node.
  2. Install apidog-cli.
  3. Expose APIDOG_ACCESS_TOKEN as an environment variable.
  4. Run apidog run with -d.

Compare parameterized testing approaches

Apidog is one option for data-driven API tests, but it is useful to know the tradeoffs.

Postman and Newman

Postman’s Collection Runner and Newman both support CSV and JSON data files. You attach a data file, reference {{column}} variables, and run the collection.

This works well, but more complex assertions usually live in JavaScript pre-request and test scripts. As the suite grows, you maintain more script code.

If you are comparing command-line runners, see Postman CLI vs Newman.

Code-first test frameworks

Frameworks like these give you full programming-language control:

  • pytest with @pytest.mark.parametrize
  • JUnit with @ParameterizedTest
  • REST Assured

They are a good fit when tests need custom setup, generated data, complex branching, or tight integration with an existing codebase.

The tradeoff is that cases usually live in code, and the team maintains the HTTP plumbing.

Apidog

Apidog keeps the scenario visual and the data external. That makes it easier for QA and developers to add cases while still running the same scenario headlessly in CI.

If your main requirement is CSV or JSON data-driven API testing, see which tool for data-driven API testing with CSV or JSON.

A practical workflow that scales

Use this workflow to introduce parameterized testing without rewriting your suite.

1. Start with one endpoint

Pick an endpoint that has caused bugs before.

Create one Apidog scenario with:

  • Variables in the request body
  • Assertions based on expected values
  • A small CSV or JSON file

Start with three rows:

code,order_total,expected_status,expected_discount
WELCOME10,100,200,10
WELCOME10,5,422,0
EXPIRED,100,410,0
Enter fullscreen mode Exit fullscreen mode

Run it in the app until all iterations behave as expected.

2. Add rows when bugs appear

When a bug report comes in, add the failing input as a new row with the correct expected output.

The bug becomes a permanent regression test without adding a new test file.

3. Move the run to CI

Add the CLI command to your pipeline:

apidog run --access-token $APIDOG_ACCESS_TOKEN \
  -t 605067 -e 1629989 \
  -d ./tests/discount-cases.csv \
  -r cli,junit --out-dir ./test-reports
Enter fullscreen mode Exit fullscreen mode

Now every pull request runs the full table.

4. Maintain logic once

When the response shape changes, update the assertion once. Every row uses the updated logic.

When you need more coverage, add rows. The test stays small while coverage expands.

Frequently asked questions

What is the difference between parameterized testing and data-driven testing?

They usually mean the same thing: running one test repeatedly with different inputs and expected outputs supplied from outside the test.

“Parameterized” emphasizes variable binding. “Data-driven” emphasizes the external data source.

Should I use CSV or JSON?

Use CSV when every case has the same flat columns.

Use JSON when the request payload contains nested objects, arrays, or structured data.

Apidog supports both formats as iteration data.

Will hundreds of iterations slow down CI?

Each row runs the scenario once, so runtime scales with row count and API latency.

If a large file slows your pipeline, split the data:

  • Run a small smoke set on every pull request.
  • Run the full set nightly or before release.

How do I keep secrets out of data files?

Do not put tokens, passwords, or credentials in CSV or JSON test data.

Use environment variables or CI secrets, for example:

$APIDOG_ACCESS_TOKEN
Enter fullscreen mode Exit fullscreen mode

Anyone with repo access can read test data files, so treat them as non-secret.

Can I run the same scenario against staging and production?

Yes. Keep the scenario and data file the same, then switch environments with -e.

Example:

# Staging
apidog run --access-token $APIDOG_ACCESS_TOKEN \
  -t 605067 -e 1629989 \
  -d ./tests/discount-cases.csv -r cli

# Production
apidog run --access-token $APIDOG_ACCESS_TOKEN \
  -t 605067 -e 9999999 \
  -d ./tests/discount-cases.csv -r cli
Enter fullscreen mode Exit fullscreen mode

This works because environment configuration and iteration data are separate inputs.

Wrapping up

Parameterized API testing turns duplicated tests into reusable scenarios plus data files. Write the test once, put each case in CSV or JSON, and let the runner execute every row.

Apidog provides the visual scenario builder, CSV and JSON iteration data, and the apidog run -d command for CI execution. Start with one endpoint, add real-world cases as rows, and run the table automatically on every change.

Top comments (0)