Automation Reporting and Debugging
Automation reporting is how a test run tells people what happened: which tests ran, which failed, how long they took and what evidence each failure left behind. Debugging a failed test is the work that follows: reading that evidence to find out why it failed and whether the cause lies in the product, the test or the environment.
For example, a nightly run reports that VAT is added to the order total failed: expected 172.50 SAR, received 150.00 SAR. The screenshot, trace and server response saved with the failure show, without re-running anything, that the server stopped applying VAT.
Why it matters
A suite is only as useful as the time it takes to act on a red result. If a failure leaves only expected true, received false, someone must re-run the test and guess the CI conditions; teams then re-run until the build is green, and real regressions pass with the noise.
Good evidence turns a failure into a short investigation: the screenshot and the trace usually show whether to fix the test, call the environment owner or report a defect. It also makes a bug report actionable, with the failing request, the response and the trace attached instead of “checkout test failed”.
Reports: one run, several readers
One run has several readers. Whoever started it reads the console. Whoever investigates a failure needs an HTML report that opens each test into its steps, error, retries and attachments. The CI server needs a machine-readable file, usually JUnit XML, to show results per test and keep their history. In Playwright, list the formats you need under reporter.
A good report answers, without asking anyone, what failed, where, in which environment and with what evidence. Name tests after the behaviour they check, wrap business-level actions in test.step() so the report reads like a test case, and attach data the screen does not show.
- Run context: build or commit, browser, base URL and start time, to match a failure with deployments and server logs.
- Flaky tests (failed, then passed on retry) counted apart from passed ones; otherwise the report hides them.
// playwright.config.ts (excerpt): one run, three readers
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: [
['list'], // the terminal
['html', { open: 'never' }], // people: playwright-report/
['junit', { outputFile: 'results/junit.xml' }], // the CI server
],
});
// checkout.spec.ts: steps and attachments make the report read like a test case
import { test, expect } from '@playwright/test';
test('VAT is added to the order total', async ({ page }, testInfo) => {
const cart = await test.step('Create a cart with one 150.00 SAR book', () =>
createCart(page, { sku: 'BOOK-01', qty: 1 })); // createCart: a project helper
await testInfo.attach('cart', {
body: JSON.stringify(cart), contentType: 'application/json',
});
await test.step('Open checkout and check the total', async () => {
await page.goto('/checkout/' + cart.id);
await expect(page.getByTestId('order-total')).toHaveText('172.50 SAR');
});
});Screenshots, video and traces: what each one shows
Keep evidence for failed tests only: each kind answers a different question and costs run time and storage.
- Screenshot: the page at the moment of failure, answering “what did the user see?”: an error banner, a spinner, a dialog over the button. Playwright:
screenshot: 'only-on-failure'; Cypress takes one automatically when a test fails incypress run; Selenium: calltakeScreenshot()(JavaScript) orgetScreenshotAs(Java) in the runner’s failure hook. - Video: the whole test, showing order and movement such as a dialog that flashed, but no DOM, network or console data. Files are large: in Playwright use
video: 'retain-on-failure'; Cypress records only when itsvideooption is enabled (off by default). - Trace: a recording you step through. A Playwright trace holds each action with DOM snapshots before and after it, the call log, console, network requests and responses, and the source line. Open it with
npx playwright show-trace, from the HTML report, or at trace.playwright.dev, which loads it in your browser without uploading it. - Logs: runner output, browser console and the server logs of the same time window, matched by timestamp or request id.
- Evidence is data: a trace can hold request headers, cookies and everything the test typed, so use synthetic data and let artifacts expire.
// playwright.config.ts (excerpt): keep evidence for failures only
import { defineConfig } from '@playwright/test';
export default defineConfig({
retries: process.env.CI ? 1 : 0,
use: {
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'on-first-retry', // needs retries; with none, use 'retain-on-failure'
},
});
// Then, in a terminal, open a trace downloaded from a CI run:
// npx playwright show-trace trace.zipDebugging a failed test: from the first error to the cause
ISTQB separates testing, which shows failures, from debugging, which finds, analyses and removes their causes. For a failed automated test, the tester’s part is failure analysis: is the defect in the product, the test code or the environment? The tester debugs the test code; a developer debugs the product, starting from the evidence.
Read the first error, not the last; later errors are usually consequences. Its type tells you which artifact to open. A setup error means the test never reached its check: read the hook’s error and the server log. A timed-out action means an element could not be found or used: open the DOM snapshot before that action and its call log. A failed assertion means the page showed something else: compare the screenshot with the expected value, then check in the network tab whether the data arrived wrong or late.
Answer: Setup: a setup step, hook or fixture failed
Outcome: Setup failure: data, sign-in or a serviceThe test never reached its check. Read the hook’s error and the server log before suspecting the feature.Answer: Action timeout: a click, fill or wait ran out of time
Question 2: In the DOM snapshot before the action, is the element there?Open the trace at that action and read its call logAnswer: No: an error page, a spinner or an empty area
Outcome: The page did not load: environment or productA failed or slow request in Network, or a script error in Console, explains it. Not a locator problem.Answer: Yes, but its role, text or test id changed
Outcome: Outdated locator or an unannounced UI changeCheck the requirement: update the test if the change is intended, report it if not.Answer: Yes, but covered, disabled or still moving
Outcome: The call log names the blockerWait for the app’s own state or handle the overlay in setup, never force the action; report a UI defect if users are blocked too.
Answer: Assertion: expected vs received differ
Question 3: Does the screenshot show the value the test received?Answer: No: it shows the expected value
Outcome: Test problem: checked too early or the wrong elementUse a retrying assertion and a locator that points at the right element.Answer: Yes: the page shows the wrong value
Question 4: In the trace’s Network tab, how did the response arrive?Answer: Wrong: the server sent the wrong data
Outcome: Back end: a defect, wrong test data or a changed settingCompare the response with the last green run; report it with the request, the response and the build range.Worked exampleAnswer: Right, but after the assertion timed out
Outcome: Slow response: overloaded environment or a performance regressionDo not raise the timeout; compare the time with the last green run and read the server log for that request.Answer: Right and on time
Outcome: Front-end defectThe page displays correct data wrongly; report it with the screenshot and the trace.
For one failure whose evidence you have. A test that passes when re-run without any change goes to the flaky-test process first.
- Reproduce locally:
npx playwright test --debugsteps through the test in the Playwright Inspector,--uiopens UI mode with a time-travel view of each run, andawait page.pause()stops at a chosen line in a headed run. Change one thing at a time, as in the lesson on reproducing failures. - Do not raise a timeout before you know why the time ran out: a slower response can itself be the defect.
Worked example: a wrong order total in the nightly run
The nightly UI run on staging, build 512, reports one failure; its retry failed too, so it is failed, not flaky. The report and trace excerpts are below, shortened.
The first error is an assertion: the element was found but showed the wrong value. The screenshot shows VAT 0.00 in the cart, so the test read what the page displayed. In the trace, the response to GET /api/cart/8812 already contains "vatRate": 0: the browser rendered what the server sent. The last green run, build 509, kept no trace, but its report still holds the cart JSON the test attaches in every run, with "vatRate": 0.15; a JSON diff of the two shows that only vatRate and total changed.
So the cause is a back-end change between builds 509 and 512 or a tax setting changed on staging; one question to the developer or the environment owner settles which. The test is right and stays as it is. The report “Cart API returns vatRate 0 for a Saudi cart since build 512” carries both responses, the trace and the build range.
✘ checkout.spec.ts:42 › checkout › VAT is added to the order total
failed (retry #1 failed too) · chromium · staging · build 512
Error: expect(locator).toHaveText(expected)
Locator: getByTestId('order-total')
Expected: "172.50 SAR"
Received: "150.00 SAR"
at checkout.spec.ts:51
Attachments: cart · screenshot · video · trace
Build 512 › trace › Network › GET /api/cart/8812 → 200
{ "subtotal": 150.00, "vatRate": 0, "total": 150.00 }
Build 509 (last green run, no trace kept) › HTML report › attachment "cart"
{ "subtotal": 150.00, "vatRate": 0.15, "total": 172.50 }Common mistakes
- Re-running a red test until it passes without reading the evidence of the failed attempt.
- Raising a timeout or adding a sleep before knowing why the time ran out.
- Changing the expected value to whatever the application now shows, without checking the requirement.
- Recording video and traces for every passing test, or recording nothing and leaving CI failures with only a message.
- Sharing reports and traces that contain real customer data, passwords or tokens.
The QA perspective
On most teams the tester takes the first look at a red automated run: classify each failure the same day, link product failures to bug reports with the trace attached, fix test problems and pass environment problems to their owner.
Report evidence, not conclusions nobody can check: the failing step, expected and received values, request and response, build range and a link to the trace. Ask what the evidence cannot answer, such as whether a UI change was intended.
Practice exercise
Classify three failed tests from a nightly report
The nightly run below has three failed tests, each with its first error and what the screenshot and the trace show. Classify each one with the decision tree from this lesson and decide what you do next.
Nightly UI run · build 731 · chromium · staging · 212 passed, 3 failed, 0 flaky
1) login.spec.ts:18 › login › shows the dashboard after sign-in
Error: locator.click: Timeout 10000ms exceeded.
Call log: getByRole('button', { name: 'Sign in' }): visible, enabled and stable
<div class="cookie-banner"> intercepts pointer events, retrying click
Screenshot: a new cookie banner covers the lower half of the sign-in form
2) profile.spec.ts:44 › profile › saves a new phone number
Error: expect(locator).toHaveText(expected)
Expected: "Saved" Received: "Saving..." Timeout: 5000ms
Trace: PUT /api/profile → 200 after 7.8 s
Server log, last green run (build 728): the same request took 0.4 s
3) orders.spec.ts:71 › orders › lists the newest order first
Error: expect(locator).toHaveText(expected)
Expected: "ORD-1042" Received: "ORD-1039"
Screenshot: the list shows ORD-1039 in the first row, ORD-1042 in the second
Trace: GET /api/orders?sort=newest → [ORD-1039 (10:02), ORD-1042 (10:05)]- Name the type of the first error: setup, action timeout or assertion.
- Name the artifact that answers the next question and what it shows.
- Classify: test problem, environment problem, front-end or back-end defect, or not yet decided.
- Write the next action: the test change, the bug title or the question to ask.
Show the model answer
Test 1, action timeout: the button is ready, but the call log and the screenshot show a new cookie banner over it. If the banner is intended, it is a test problem: set or accept consent in the shared setup and never force the click. Also ask whether users on small screens face the same covered button; if so, report a UI defect.
Test 2, assertion: Saving... shows the page was still waiting. The trace says why: the save request succeeded, but late, after 7.8 s, against 0.4 s in build 728 and a 5 s assertion timeout. Raising the timeout would hide a slowdown users feel. Not yet decided: the server logs for that request, found by the trace time, tell an overloaded staging environment from a regression in build 731.
Test 3, assertion: the page displays what it received, and the trace shows that the API itself returned ORD-1039 (10:02) before ORD-1042 (10:05) for sort=newest. The back end sorts in the wrong direction: report “Orders API with sort=newest returns the oldest order first” with the request, the response and the trace.
Frequently asked questions
Should I record traces and video for every test?
Usually not: recording everything slows the run and produces large files nobody opens. A common setup is one retry in CI with trace: 'on-first-retry', or 'retain-on-failure' when there are no retries (with zero retries, on-first-retry records nothing), plus video and screenshots only on failure. Turn on full recording only while hunting a specific problem.
Is debugging the tester’s job or the developer’s?
Both, for different defects. ISTQB treats testing and debugging as separate activities: testing shows failures; debugging finds, analyses and removes their causes. The tester analyses an automated failure far enough to show where the defect is and debugs the test code; a developer debugs the product code, starting from the evidence the tester attached.
I use Selenium or Cypress. Does this still apply?
Yes; only the recording differs. Cypress takes screenshots on failure in cypress run, records video when the video option is enabled, and shows a snapshot per command in the Command Log of cypress open. Selenium WebDriver records nothing by itself: take a screenshot and save the page source in the runner’s failure hook, and log each step with a timestamp. The reading order stays the same: first error, then evidence, then comparison.
Where do I start when many tests fail in the same run?
Look for a shared cause first. If many tests fail with the same first error at the same step, such as sign-in or a setup hook, the cause is usually the environment or a shared fixture, not each test. Fix or report it once and re-run; the CI/CD testing guide has a triage flow for pipeline-level failures.
References
- ISTQB Certified Tester Foundation Level (CTFL) v4.0The foundation syllabus of software testing: test levels and types, test design techniques, test management and tools.
- Certified Tester Advanced Level Test Automation Engineering (CTAL-TAE) v2.0ISTQB's advanced syllabus on designing, implementing and maintaining test automation solutions and their architecture.
- Trace viewer — PlaywrightRecording and replaying a test run step by step — DOM snapshots, network, console — to diagnose failures, especially from CI.
- Cypress DocumentationOfficial documentation for end-to-end and component testing in the browser with Cypress.
Practise with Mutqan tools
Compare two JSON documents semantically and highlight added, removed and changed values.
Compare two texts line by line or word by word and highlight differences.
Pretty-print XML with configurable indentation, and minify it back.
Convert timestamps (seconds, milliseconds, microseconds) to human dates and back, in any timezone.
Explain stack traces and error messages, identify likely root causes and next debugging steps with Mutqan AI.
Go deeper
- CI/CD TestingTest reports and artifacts
- CI/CD TestingPipeline failure analysis: a triage flow
- Flaky TestsDiagnosing a flaky test
- How to Write a Bug ReportHow to write a bug report developers can act on: the fields that matter, a copy-ready template, a worked example, severity vs priority, and mistakes to avoid.
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.