QA How-To
Newman htmlextra Report Tutorial for Postman Collections
Newman htmlextra report tutorial: install the reporter, run a Postman collection, inspect HTML failures, protect data, and publish CI artifacts reliably.
17 min read | 3,055 words
TL;DR
Install Newman and newman-reporter-htmlextra locally, then run `npx newman run collection.json -r cli,htmlextra --reporter-htmlextra-export reports/report.html`. Confirm the HTML exists and use Newmans exit status to decide whether the assertions passed.
Key Takeaways
- Install Newman and htmlextra in the same npm project so reporter discovery is reliable.
- Run with -r cli,htmlextra to keep terminal feedback while exporting HTML to a fixed path.
- Add named Postman assertions before judging the report useful.
- Use CSV iteration data to trace failures to individual input rows.
- Treat Newmans exit code as the quality gate and HTML as the diagnostic artifact.
- Export JUnit or JSON alongside HTML when CI needs machine-readable results.
A Newman htmlextra report tutorial should end with a real HTML file you can open, inspect, and keep as a CI artifact. Install Newman and its community htmlextra reporter in the same npm project, run a Postman collection with -r cli,htmlextra, and set --reporter-htmlextra-export to a predictable path. The command's exit status still decides whether the run passed; the HTML explains what happened.
This walkthrough builds a tiny local API and a two-request Postman collection. You will produce a passing dashboard, add iteration data, deliberately trigger a failure, and package the same workflow for CI. The example avoids credentials and third-party endpoints, so every request and assertion is under your control. If you already have a collection, substitute its exported JSON after you understand the baseline.
The reporter is maintained outside Newman's built-in set. That distinction matters when diagnosing installation problems and choosing formats for automation. For the underlying runner workflow, see the Postman and Newman in CI guide. For variable precedence in a larger collection, use the Postman collection variables and scopes guide.
What You Will Build
- A local HTTP service with
/healthand/items/:idendpoints, so the tutorial works without an account or network fixture. - An exported Postman Collection v2.1 JSON file with status, content-type, and payload assertions.
- An HTML dashboard at
reports/api-smoke.html, plus optional JSON and JUnit outputs for machines. - A CSV-driven two-iteration run that makes the report's iteration view useful.
- A shell entry point that retains Newman's failure exit code while leaving reports available for CI artifact upload.
The final report is meant for human review. Its request details can contain response data, so decide what to hide before publishing it. The terminal result and machine-readable formats serve separate purposes, which the table below makes explicit.
| Output | Reader | Strength | Limitation |
|---|---|---|---|
| htmlextra HTML | Tester or reviewer | Browse requests, assertions, iterations, and failures | HTML is an artifact, not a stable test-result API |
| Newman CLI | Developer watching a run | Immediate request and assertion feedback | Console history may disappear after a CI job |
| Newman JSON | Automation or analysis script | Structured run summary | Requires code to make it readable |
| Newman JUnit XML | CI test-results interface | Fits common test-report ingestion | May omit the narrative request context you need to debug |
Prerequisites
Use a current Node.js installation supported by your selected Newman release. Newman's published README specifies Node.js 16 or newer as its minimum; if your installed package declares a stricter engines range, follow that package. You also need npm, a terminal, and a browser for opening the resulting HTML. curl is used only to verify the local service. On Windows, run the shell snippets in a Bash environment such as Git Bash or WSL; PowerShell uses different here-document and process syntax.
Record the exact versions in your project rather than copying a version number from an article. Run node --version and npm --version, then after installation run npx newman --version and npm ls newman newman-reporter-htmlextra --depth=0. Commit package-lock.json if this becomes a team or CI workflow. npm ci will then install the precise resolved versions in that lockfile. The examples below intentionally use no invented package pin or Docker tag.
A clean directory is helpful because reporter output and sample files are easy to identify. Do not create this demo inside a production test repository unless you want to keep these files. The local server listens on 127.0.0.1:3007; choose another unused port and update the commands if that port is already occupied.
Step 1: Prepare the Newman htmlextra Report Tutorial Workspace
Create one npm project and install both packages locally. A local install makes Newman's reporter discovery predictable: the npx command resolves the project runner, and that runner finds the reporter beside it in node_modules. Mixing a globally installed Newman with a locally installed reporter is a common cause of "reporter not found" errors.
mkdir newman-htmlextra-demo
cd newman-htmlextra-demo
npm init -y
npm install --save-dev newman newman-reporter-htmlextra
mkdir -p reports
Keep the generated package.json and package-lock.json. You may later add a named npm script, but first use the complete Newman command so every input and output is visible. Installing the reporter does not change the collection itself; it only adds a reporting plugin that Newman loads when htmlextra appears in -r.
Verify Step 1: Run the commands below. The package tree must list both packages without an unmet dependency error. The version printed by npx newman --version is the actual runner you will use, not necessarily a globally installed executable.
node --version
npm --version
npx newman --version
npm ls newman newman-reporter-htmlextra --depth=0
If npm reports an engine warning, compare your Node.js version with the installed packages' engines fields before continuing. Fix that mismatch at the source; suppressing warnings can leave you with a toolchain that works locally and fails in CI.
Step 2: Start a Deterministic API Fixture
Create a small Node server with two paths. /health proves connectivity and returns a fixed status object. /items/1 and /items/2 return distinct records for the later CSV iteration. Unknown item IDs return 404, giving you a safe way to exercise a failure without changing the collection script.
cat > server.cjs <<'JS'
const http = require('node:http');
const items = {
'1': { id: 1, name: 'pencil' },
'2': { id: 2, name: 'notebook' }
};
http.createServer((request, response) => {
response.setHeader('content-type', 'application/json; charset=utf-8');
if (request.method === 'GET' && request.url === '/health') {
response.writeHead(200);
response.end(JSON.stringify({ status: 'ok' }));
return;
}
const match = request.method === 'GET' && /^\/items\/(\d+)$/.exec(request.url);
if (match && items[match[1]]) {
response.writeHead(200);
response.end(JSON.stringify(items[match[1]]));
return;
}
response.writeHead(404);
response.end(JSON.stringify({ error: 'not found' }));
}).listen(3007, '127.0.0.1', () => {
console.log('Demo API listening on http://127.0.0.1:3007');
});
JS
node server.cjs > server.log 2>&1 &
SERVER_PID=$!
Leave that process running through Step 7. The process ID is held in this shell session; kill "$SERVER_PID" stops it afterward. If you close the terminal, find and stop the process using your operating system's process tools. Avoid replacing the example with a public endpoint merely to save a few lines: an unrelated API outage can make a correct collection look broken.
Verify Step 2: Check both successful routes and the intentional 404. The first call returns {"status":"ok"}, the item call returns {"id":1,"name":"pencil"}, and the final call prints HTTP status 404.
curl -fsS http://127.0.0.1:3007/health
curl -fsS http://127.0.0.1:3007/items/1
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3007/items/999
A failed first curl means Newman will fail too. Inspect server.log, confirm the chosen port is free, and start the fixture again before building the collection.
Step 3: Export a Collection With Meaningful Assertions
Write a minimal Postman Collection v2.1 file. Each request has a test event containing JavaScript understood by the Postman sandbox. The health script checks HTTP 200 and the exact status field. The item script checks HTTP 200, JSON content type, and the ID requested for that iteration. These assertions create readable rows in htmlextra, whereas an untested collection produces a visually impressive report with little QA value.
cat > collection.json <<'JSON'
{
"info": {
"name": "Local API smoke",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"variable": [
{ "key": "baseUrl", "value": "http://127.0.0.1:3007" },
{ "key": "itemId", "value": "1" }
],
"item": [
{
"name": "Health is ready",
"request": {
"method": "GET",
"url": "{{baseUrl}}/health"
},
"event": [{
"listen": "test",
"script": {
"exec": [
"pm.test('Health returns 200', () => pm.response.to.have.status(200));",
"pm.test('Health says ok', () => pm.expect(pm.response.json().status).to.eql('ok'));"
]
}
}]
},
{
"name": "Item matches requested ID",
"request": {
"method": "GET",
"url": "{{baseUrl}}/items/{{itemId}}"
},
"event": [{
"listen": "test",
"script": {
"exec": [
"pm.test('Item returns 200', () => pm.response.to.have.status(200));",
"pm.test('Item is JSON', () => pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json'));",
"pm.test('Item ID matches input', () => pm.expect(pm.response.json().id).to.eql(Number(pm.variables.get('itemId'))));"
]
}
}]
}
]
}
JSON
The info.schema URL identifies the collection format; it is not a request that Newman sends during the run. Collection variables supply defaults, so the first run needs no environment file. Later, CSV iteration data supplies itemId values per row. Keep request names specific because the HTML report uses them to organize results.
Verify Step 3: Parse the JSON and inspect its shape before running HTTP requests. You should see two items and the default item ID of 1.
node -e "const c=require('./collection.json'); console.log(c.info.name, c.item.length, c.variable.find(v=>v.key==='itemId').value)"
If this command throws a JSON parse error, fix the collection file first. Newman cannot report assertions for a collection it cannot load.
Step 4: Generate the First Newman htmlextra Report Tutorial HTML
Run the collection with both cli and htmlextra reporters. Explicitly listing cli matters: once you select another reporter, Newman does not automatically keep terminal output. The export flag gives the HTML a stable filename instead of leaving you to search the default newman/ directory. Newman's built-in reporter documentation confirms the comma-separated syntax, while the htmlextra README documents its export flag.
npx newman run collection.json \
-r cli,htmlextra \
--reporter-htmlextra-export reports/api-smoke.html
Expect two requests and five passing assertions: two on health, three on the item. The CLI summary is your immediate pass/fail signal. The HTML is the review artifact. Open it in a browser and expand each request to see its response and named tests. If you see a dashboard but no meaningful tests, revisit the event arrays rather than adding more reporter flags.
Verify Step 4: Confirm the export is nonempty and resembles HTML. This check validates artifact creation, not assertion success, so still inspect Newman's exit code and summary.
test -s reports/api-smoke.html
node -e "const s=require('node:fs').readFileSync('reports/api-smoke.html','utf8'); if(!/<html[\\s>]/i.test(s)) process.exit(1); console.log('HTML report present')"
On macOS, open reports/api-smoke.html opens the file. On Linux, use your desktop's opener; on Windows, open it through File Explorer. Browser interaction is the best way to confirm navigation and request details are usable, because a file can be valid HTML yet contain an unhelpful run.
Step 5: Customize the Newman htmlextra Report Tutorial Output
Give the report a recognizable title and reduce accidental data exposure. The htmlextra README documents --reporter-htmlextra-title, --reporter-htmlextra-browserTitle, and --reporter-htmlextra-skipSensitiveData. For this example, the last flag hides request and response headers and bodies while leaving the request summary and tests visible. That is a sensible default when a report may be downloaded from CI by people who did not run the test.
npx newman run collection.json \
-r cli,htmlextra \
--reporter-htmlextra-export reports/api-smoke-private.html \
--reporter-htmlextra-title 'Local API Smoke' \
--reporter-htmlextra-browserTitle 'API Smoke Results' \
--reporter-htmlextra-skipSensitiveData
Use the broad privacy switch deliberately. Hiding payloads makes some failures harder to diagnose, especially schema mismatches. For an internal report with no sensitive bodies, you might instead use --reporter-htmlextra-skipHeaders 'Authorization' or --reporter-htmlextra-omitResponseBodies. Redacting only a displayed header is not a license to upload the raw Newman JSON elsewhere: every output format has its own contents. Review a generated report with representative, nonproduction data before treating it as safe to share.
Verify Step 5: The new file should be nonempty, contain the custom title, and still present the named test results. Open the HTML as well, because text search cannot verify that a section is rendered correctly.
test -s reports/api-smoke-private.html
node -e "const s=require('node:fs').readFileSync('reports/api-smoke-private.html','utf8'); if(!s.includes('Local API Smoke')) process.exit(1); console.log('Custom title present')"
For a collection with credentials, use nonsecret environment inputs in the demo and keep secrets in your CI secret store. Reporter privacy settings reduce exposure in an artifact; they do not protect credentials in logs, exported collections, or other files automatically.
Step 6: Run Data-Driven Iterations and Compare Results
A single pass confirms your happy path, but report navigation becomes more valuable when requests repeat with different inputs. Create a CSV with two item IDs. Newman's --iteration-data reads each row, and itemId from the row supplies the value used in the request URL and assertion. This builds on the same collection without duplicating requests. The Postman data-driven testing guide covers broader dataset design.
cat > items.csv <<'CSV'
itemId
1
2
CSV
npx newman run collection.json \
--iteration-data items.csv \
-r cli,htmlextra \
--reporter-htmlextra-export reports/api-items.html \
--reporter-htmlextra-title 'Items by Iteration'
You should now have two iterations, four HTTP executions, and ten passing assertions. Do not confuse four executions with four distinct collection requests: the same two items ran once for each CSV row. The htmlextra iteration view helps answer which input caused a failure when a larger dataset contains many rows. Use stable identifiers in test names and data columns so someone reviewing the report can trace an outcome back to a row.
Verify Step 6: Check the CSV row count and the artifact. Then open reports/api-items.html and switch between iterations; item ID 1 and item ID 2 should each pass the matching assertion.
node -e "const fs=require('node:fs'); const lines=fs.readFileSync('items.csv','utf8').trim().split(/\\r?\\n/); if(lines.length!==3) process.exit(1); console.log('Two data rows')"
test -s reports/api-items.html
If both iterations hit the same URL, check the CSV header spelling and the {{itemId}} token. A typo can fall back to the collection's default value and produce a misleading green report.
Step 7: Produce a Failing Report Without Losing the Exit Code
Test your failure path before trusting a CI workflow. Supply an unknown item ID as an environment variable for a one-iteration run. Environment variables override the collection default, and the server returns 404. The item status assertion fails; the subsequent JSON ID assertion also fails because the error payload has no id. The health request remains green, making the report useful for isolating the problem.
set +e
npx newman run collection.json \
--env-var itemId=999 \
-r cli,htmlextra \
--reporter-htmlextra-export reports/api-failure.html
RUN_STATUS=$?
set -e
printf 'Newman exit status: %s\n' "$RUN_STATUS"
test -s reports/api-failure.html
test "$RUN_STATUS" -ne 0
This command group is an inspection exercise: it confirms the failing run returns a nonzero status and leaves an HTML artifact. Do not add Newman's --suppress-exit-code to a quality gate merely to preserve the report. A CI job that goes green after failed assertions hides the reason you ran the collection. Likewise, || true on the Newman command discards the meaningful exit status unless you capture it first.
Verify Step 7: Open reports/api-failure.html. Find the item request, its 404 response, and the failed assertion names. Compare it with the CLI failure section. If the file is absent, inspect the reporter installation or export path; if the exit status is zero, inspect whether the test event ran and whether the variable reached the request URL.
Step 8: Package the Run for CI and Machine Readers
Make a short shell script that starts the same fixture, executes the passing collection, and retains Newman's status. It also exports JSON and JUnit in the same invocation. JSON is useful for custom analysis; JUnit is for a CI service's test-results view. HTML remains the link a reviewer can open. The test automation CI/CD guide explains how to fit this gate into a wider pipeline.
cat > run-ci.sh <<'SH'
#!/usr/bin/env bash
set -u
mkdir -p reports
node server.cjs > server.log 2>&1 &
server_pid=$!
trap 'kill "$server_pid" 2>/dev/null || true' EXIT
for attempt in 1 2 3 4 5; do
if curl -fsS http://127.0.0.1:3007/health > /dev/null; then break; fi
sleep 1
done
if ! curl -fsS http://127.0.0.1:3007/health > /dev/null; then
cat server.log
exit 1
fi
npx newman run collection.json \
-r cli,htmlextra,json,junit \
--reporter-htmlextra-export reports/api-smoke.html \
--reporter-htmlextra-skipSensitiveData \
--reporter-json-export reports/api-smoke.json \
--reporter-junit-export reports/api-smoke.xml
run_status=$?
printf 'Newman exit status: %s\n' "$run_status"
exit "$run_status"
SH
chmod +x run-ci.sh
Stop the fixture started in Step 2 before trying the script, because the script needs port 3007. Run kill "$SERVER_PID" in that original terminal, then execute the script. In CI, run npm ci first so the committed lockfile determines exact dependency versions. Configure your CI platform to upload reports/ even when ./run-ci.sh exits nonzero; that upload rule belongs to the platform, not to Newman. The script's trap stops its fixture on exit, including failed tests.
Verify Step 8: The script should exit zero for the provided collection and create three nonempty report files. A second run should also work, showing that cleanup released the port.
kill "$SERVER_PID"
./run-ci.sh
test -s reports/api-smoke.html
test -s reports/api-smoke.json
test -s reports/api-smoke.xml
./run-ci.sh
If your pipeline tests a deployed API instead of this fixture, remove the server startup and supply an environment file or --env-var baseUrl=... to Newman. Keep the report paths and exit-code handling. Never assume a generated HTML file means the assertions passed; a failed run can still generate a useful report.
Give each CI job its own report directory when jobs run concurrently. Otherwise two runs can overwrite api-smoke.html, leaving a reviewer with a file from the wrong build. Put a build identifier in the artifact name at the CI upload layer, and keep the three formats from one Newman invocation together. Set an artifact retention period that matches your team's debugging window. If a run fails before Newman starts, such as an npm install failure or a dead API fixture, there may be no report to upload; the console log is the primary evidence for that earlier stage.
Troubleshooting
Problem: Newman says it cannot find reporter htmlextra -> Install newman and newman-reporter-htmlextra in the same local project, then invoke npx newman. Check npm ls ... --depth=0 and confirm you are in the directory containing package.json. A global Newman process does not automatically resolve a reporter installed only in this project's node_modules.
Problem: The run finishes but no HTML file appears -> Use -r cli,htmlextra, pass --reporter-htmlextra-export reports/api-smoke.html, and check the current working directory. A bare htmlextra run writes under newman/ by default. If the collection cannot load or the process exits before reporter initialization, fix the earlier terminal error first.
Problem: The report exists but has no assertions -> Open collection.json and confirm each request has an event with listen: "test" and executable script.exec lines. A successful HTTP status alone is not a Postman test. The demo deliberately names each assertion so the reporter can show what was checked.
Problem: Item requests fail with connection refused -> Verify curl -fsS http://127.0.0.1:3007/health outside Newman. If the server never started, read server.log; if port 3007 is occupied, stop that process or change the server port and baseUrl together. A report flag cannot repair a networking failure.
Problem: CSV runs repeat one item or fail only on one row -> Match the itemId header exactly, keep numeric IDs in the data file, and inspect the request URL for each iteration. Iteration data is intended to vary inputs; a stale variable or a different header can leave the collection default in place. Use the iteration view to identify the row before changing assertions.
Problem: CI is green despite failed tests, or red without an artifact -> Remove --suppress-exit-code and avoid bare || true. Capture Newman's status, upload reports/ under your CI platform's always-run artifact rule, then exit with the captured status. Confirm this behavior with the intentional 404 run from Step 7.
Interview Questions and Answers
The hands-on run gives you concrete evidence for an interview answer: you can explain reporter discovery, exit codes, variable input, and artifact handling rather than reciting a flag list. The interviewQnA field below contains six focused model answers that can be used for practice. When discussing report design, distinguish a human-readable dashboard from a machine-consumed JUnit or JSON result.
Common Mistakes
- Treating a created HTML file as proof of success. Read Newman's exit status and assertion summary separately.
- Installing the runner globally and the reporter locally, then debugging the collection when reporter resolution fails.
- Publishing raw request and response details from an authenticated collection without reviewing the artifact.
- Keeping only an HTML report in CI when the service expects JUnit XML to populate its test-results interface.
- Changing the collection for every data row instead of using
--iteration-dataand checking the iteration in the report. - Relying on a default output directory when an artifact uploader expects a stable path.
Where To Go Next
Replace the demo endpoints with a test environment that is safe for automated runs, then keep the same report command and verify it on one failure case. If the collection grows, split related requests into folders and make each assertion name describe observable behavior. For examples of request setup, read Postman pre-request scripts. For a broader tool comparison, see Karate vs Postman.
Use the HTML report for investigation, JUnit for a CI results surface, and JSON when you need structured post-processing. If you are preparing to explain the workflow to an interviewer, the Postman interview questions guide provides more collection and environment scenarios. The local fixture can also be extended with POST, authentication, and negative cases once its baseline remains reliable.
Conclusion
You now have a repeatable Newman run that generates an htmlextra dashboard from a tested Postman collection, shows multiple data iterations, and preserves failure status for CI. The key command is npx newman run collection.json -r cli,htmlextra --reporter-htmlextra-export reports/api-smoke.html; the other steps make its result trustworthy and reviewable.
Start with your smallest real collection, pin its toolchain through the lockfile, and inspect one passing and one failing report before sharing artifacts with your team.
Interview Questions and Answers
Why would you use htmlextra rather than only the Newman CLI reporter?
The CLI gives immediate feedback, but htmlextra produces a browsable artifact with request, assertion, and iteration detail. I would run both reporters so engineers can see live output and reviewers can inspect the saved run. I would still use the process exit status as the gate.
How does Newman discover a community reporter?
A reporter is installed as a Node package named `newman-reporter-<name>`, and Newman loads it when that name appears in the reporter list. For htmlextra, I install `newman-reporter-htmlextra` beside local Newman and pass `-r cli,htmlextra`. That avoids relying on global module paths.
What would you check if an HTML report has requests but no tests?
I would inspect the collection items for `test` events and executable Postman script lines. A request returning 200 is not automatically an assertion. I would add named checks for status and response content, rerun Newman, and confirm those names appear in the report.
How would you handle failed tests and artifact upload in CI?
I would capture Newmans nonzero exit status, arrange for the CI platform to upload the report directory even on failure, and exit the job with the captured status. I would not use `--suppress-exit-code` for a quality gate. I would test that behavior with a deliberate failing assertion.
When do you export JUnit and JSON alongside htmlextra?
I use JUnit when the CI service has a test-results viewer, and JSON when a script needs structured run data. The htmlextra file is for a person diagnosing requests and assertions. Producing all three from one run keeps their data aligned.
How can a report show which data row caused an API failure?
Pass a CSV or JSON data file through Newmans `--iteration-data` option and use a distinct row identifier in the request or assertion context. htmlextra separates run iterations, so I can inspect the failing iteration and its request details. I also verify variable spelling to avoid silently using a collection default.
What privacy review would you perform before publishing htmlextra HTML?
I would run with representative data, inspect request and response sections, and decide whether bodies or headers must be hidden. The reporter offers `skipSensitiveData` and narrower omission options, but other outputs and logs need separate review. I would keep credentials in a secret store rather than in an exported collection.
Frequently Asked Questions
How do I install htmlextra for Newman?
Run `npm install --save-dev newman newman-reporter-htmlextra` in one npm project, then use `npx newman`. Keeping the runner and external reporter together avoids module resolution surprises.
What command exports a Newman htmlextra HTML report?
Use `npx newman run collection.json -r cli,htmlextra --reporter-htmlextra-export reports/report.html`. The `cli` entry retains terminal output, and the export flag fixes the artifact path.
Where does htmlextra put its report by default?
Without an explicit export path, htmlextra writes a report under the `newman/` directory in the current working directory. Set `--reporter-htmlextra-export` when CI or a teammate needs a predictable filename.
Can a failed Newman run still create an HTML report?
Yes. A report can be generated even when assertions fail, which makes it valuable for debugging. Check the Newman process exit status separately and preserve the artifact under an always-run CI upload rule.
How do I hide sensitive data from an htmlextra report?
Use `--reporter-htmlextra-skipSensitiveData` to omit request and response headers and bodies while retaining summary and test information. Review representative output before sharing it, and handle other exported formats independently.
Does htmlextra replace JUnit in CI?
No. htmlextra is a human-readable HTML dashboard, while JUnit XML is commonly ingested by CI test-results interfaces. Newman can emit both in one run using `-r cli,htmlextra,junit` and separate export paths.
Why does Newman say it cannot find the htmlextra reporter?
The reporter package is usually outside the module path of the Newman executable being used. Install both packages locally and invoke the local runner with `npx newman`, then confirm the packages with `npm ls`.