On this page10 sections

Data-Driven API Testing

Drive a Postman collection through multiple iterations using a CSV data file — parameterising request bodies and assertions, running the suite in the Collection Runner, and executing it via Newman on the command line for CI-readiness.

Role

Manual QA engineer

Difficulty

Intermediate

Time limit

~90 min

Category

api client

Postman (free desktop app)Newman (npm install -g newman)

Scenario

Your team needs to validate that the Reqres.in POST /api/users endpoint handles a variety of inputs correctly — different user names, job titles, and edge-case values including empty strings and special characters. Running each variation manually by editing the request body is slow and error-prone. Your task is to create a CSV data file containing multiple input rows, parameterise the POST /api/users request to read from that file, and run all iterations in a single Collection Runner pass. You will also verify that the runner correctly fails when a data row produces an unexpected result, and run the same suite from the command line using Newman to demonstrate that it can be integrated into a CI pipeline.

Requirements

  • 1.Create a CSV data file (users-data.csv) with at least 5 rows and the following columns: name, job, expectedStatus; include at minimum: two valid rows (different name/job values), one row with an empty name (''), one row with a long name (>100 characters), and one row with a special-character name (e.g. 'QA Engineer <script>') — note that Reqres.in accepts all inputs and returns 201 for all; set expectedStatus to 201 for valid rows and document your expectation for edge-case rows
  • 2.Parameterise the POST /api/users request body to use data-file variables: { "name": "{{name}}", "job": "{{job}}" } — in the Collection Runner these double-brace references are resolved from the current iteration's data row
  • 3.Add a Tests-tab assertion that uses the data file variable for the expected status: pm.test('Status is ' + data.expectedStatus, () => pm.response.to.have.status(parseInt(data.expectedStatus))); also assert that the response body echoes back the name and job values from the data row
  • 4.Run the collection in the Postman Collection Runner, selecting users-data.csv as the data file; capture the runner summary showing the iteration count, passed/failed test count per iteration, and total pass/fail
  • 5.Install Newman globally (npm install -g newman) and run the same collection from the command line: newman run 'Reqres API.json' --data users-data.csv; capture the terminal output showing the per-iteration results
  • 6.Write a short CI-integration note (4–6 sentences) explaining how Newman would fit into a CI pipeline (e.g. GitHub Actions, Jenkins): where the collection and data files would be stored, how the newman command would be invoked, and what exit code Newman returns on failure (exit code 1 when any test fails) so the pipeline step fails correctly

Starter data

  • CSV header row: name,job,expectedStatus — all column names are referenced in scripts as data.name, data.job, data.expectedStatus
  • Sample CSV rows: QA Engineer,API tester,201 | Senior QA,Team lead,201 | (empty),API tester,201 | AAAAA...A (101 chars),QA,201 | QA <script>alert(1)</script>,tester,201 — Reqres.in does not validate input and returns 201 for all these; the point is to observe how the API behaves and confirm the response echoes the submitted values
  • Data variables in tests: in the Tests tab, the current iteration's row is accessible as the data object — data.name, data.job, data.expectedStatus; note that data is only available in the Tests and Pre-request Scripts tabs, not in the URL or headers (use environment/collection variables for those)
  • Newman install: npm install -g newman (requires Node.js ≥ 18); run: newman run collection.json --data data.csv --reporters cli; Newman exits with code 0 on all-pass and code 1 on any failure — most CI systems treat non-zero exit as a failed step
  • Collection Runner data file format: Postman accepts CSV (comma-separated, UTF-8, first row is column headers) and JSON (array of objects); CSV is more widely used for tabular test data
  • Iteration order: the Collection Runner runs all requests in folder order once per data row; with 5 rows, a collection with 1 folder containing 1 request produces 5 iterations = 5 request executions

Expected deliverables

  • A CSV data file (users-data.csv) with at least 5 rows: header row + 4 valid/edge-case rows with name, job, and expectedStatus columns
  • Updated POST /api/users request with parameterised body ({{name}}, {{job}}) and Tests-tab assertions using data variables
  • Postman Collection Runner summary screenshot or text output: iteration count (5), per-iteration pass/fail, total tests passed and failed
  • Newman terminal output showing the same 5 iterations run from the command line
  • A CI-integration note (4–6 sentences) explaining storage, invocation, and Newman exit-code behaviour

Evaluation rubric

DimensionWhat reviewers look for
Data-file designDoes the CSV cover meaningfully different input scenarios, not just variations on the same valid input? Including only five rows with different names but all valid values misses the purpose of data-driven testing: the data file should contain boundary values, empty strings, special characters, and edge cases that would require separate test cases if written manually. Each row should represent a distinct test scenario with a documented expectation.
Parameterisation correctnessAre request body variables ({{name}}, {{job}}) and test data variables (data.expectedStatus) used correctly and in the right places? Collection Runner data variables ({{name}}) resolve in the request URL, body, and headers. The data object (data.name) is only available in pre-request and test scripts. Mixing up these two access patterns — using {{name}} in a test script or data.name in a request body — will produce unresolved placeholders or undefined values.
Iteration coverageDid the runner actually execute all 5 iterations, and does the summary distinguish per-iteration results? A summary showing '5 iterations, 15 passed' confirms each iteration ran correctly. A summary showing '1 iteration, 3 passed' means only the first data row was used — the data file was not loaded or the runner was configured with iterations: 1. Each iteration's test results must be visible in the runner output.
Newman CLI usageDoes the Newman command specify both the collection and the data file? Running newman run collection.json without --data data.csv runs only a single iteration with no data-file variables — {{name}} resolves to an empty string. The terminal output must show 5 iterations. Is Newman's exit code behaviour documented — important for CI integration where a failing test must fail the pipeline step?
CI-readiness awarenessDoes the CI-integration note address the practical questions a team would ask before adding Newman to a pipeline: where the collection and data files are stored (repository, not local disk), how secrets (base URL, credentials) are injected without hardcoding (environment variables or a CI-managed environment JSON file), and what happens when Newman fails (exit code 1 stops the pipeline)?

Sample solution outline

  • users-data.csv: name,job,expectedStatus / QA Engineer,API tester,201 / Senior QA,Team lead,201 / ,Tester,201 / AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,QA,201 / QA <script>alert(1)</script>,Engineer,201 — note: Reqres accepts all; the empty-name and script-injection rows confirm the API echoes back exactly what was sent without sanitisation, which may be a documentation finding on a real API
  • POST /api/users body: { "name": "{{name}}", "job": "{{job}}" }; Tests tab: const j = pm.response.json(); pm.test('Status ' + data.expectedStatus, () => pm.response.to.have.status(parseInt(data.expectedStatus))); pm.test('name echoed back', () => pm.expect(j.name).to.equal(data.name)); pm.test('job echoed back', () => pm.expect(j.job).to.equal(data.job));
  • Collection Runner summary: 5 iterations, 15 tests total (3 per iteration), 15 passed, 0 failed; iteration 3 (empty name) shows j.name === '' — PASS because Reqres echoes back the empty string; this is an observation worth noting: a real API might return a validation error
  • Newman output: newman run 'Reqres API.postman_collection.json' --data users-data.csv; output shows: iteration 1-5, 3 tests each, ✓ Status 201 ✓ name echoed back ✓ job echoed back for all rows; summary: 5 iterations, 15 assertions, 0 failures; exit code 0
  • CI-integration note: store the collection JSON and data file in the repository under /tests/api/; in GitHub Actions, add a step: 'run: newman run tests/api/reqres-collection.json --data tests/api/users-data.csv'; inject baseUrl as an environment variable using --env-var baseUrl=${{ secrets.API_BASE_URL }}; Newman exits with code 1 if any assertion fails, which causes the GitHub Actions step to fail and blocks the PR merge; add newman as a devDependency (npm install --save-dev newman) so CI does not require a global install

Common mistakes

  • Forgetting to select the data file in the Collection Runner — running the runner without a data file runs a single iteration with no data-file variables, leaving {{name}} and {{job}} unresolved (rendered as empty strings) and making the data.expectedStatus reference undefined
  • Using data.name in the request body field instead of {{name}} — data.name is only available in pre-request and test scripts; in the request body, URL, or headers, use the double-brace syntax {{name}}; using data.name in a body field renders the literal string 'data.name'
  • Designing a data file with only valid, similar rows — five rows of valid user names and jobs is a parameterised happy path, not data-driven testing; the value of a data file is in testing boundary values, empty inputs, and special characters in a single runner pass
  • Not running Newman from the command line and substituting a second Postman Runner screenshot — the Newman CLI step is specifically about verifying the collection runs outside of Postman, which is what CI systems use; a second Postman screenshot does not demonstrate CI-readiness
  • Hardcoding the base URL in the Newman command instead of using an environment file — newman run collection.json --data data.csv works but bakes the URL into the command; in CI, use --environment staging-env.json or --env-var baseUrl=https://... so the URL is configurable without editing the command
  • Reporting that Newman cannot be used because it requires a paid Postman account — Newman is a free, open-source CLI tool (github.com/postmanlabs/newman) that runs any Postman collection JSON file regardless of account type; it requires only Node.js

Submission checklist

  • CSV data file (users-data.csv) with header row and at least 5 data rows including at least one edge case
  • POST /api/users request body parameterised with {{name}} and {{job}}
  • Tests-tab assertions using data.expectedStatus and data variable echo checks
  • Postman Collection Runner summary showing 5 iterations and per-iteration pass/fail
  • Newman terminal output showing 5 iterations run from the command line
  • Newman install command and full run command documented
  • CI-integration note (4–6 sentences) covering storage, invocation, exit code, and secret injection

Extension ideas

  • +Add a second request to the data-driven folder — GET /api/users/{{userId}} — and add a userId column to the CSV file; verify that each iteration retrieves the correct user based on the CSV row, then add an assertion that the email field matches an expectedEmail column in the data file
  • +Run Newman with the --reporters cli,htmlextra reporter (npm install -g newman-reporter-htmlextra) to generate a standalone HTML report; open it and note which information it shows that the CLI output does not — this is the report format most useful for a QA team that does not have access to the terminal
  • +Modify the data file to include one row with expectedStatus set to 400 and a request body designed to trigger a validation error on a real API; document what happens when Reqres.in (which does not validate) returns 201 instead of the expected 400, and explain how you would handle this in a data-driven suite for a real API