Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To take a screenshot only when a Playwright .NET test fails, check the test outcome in the runner’s teardown or cleanup hook and call Page.ScreenshotAsync before the runner disposes the page or its context. The screenshot API captures the page; it does not know whether a test passed. Save the image to a unique path, or keep the returned byte[] and hand it to your test runner or CI artifact system.

How failure-only screenshots work

The reliable pattern has three parts: obtain the test result from the test framework, capture while the Playwright Page is still alive, and store the image somewhere your local workflow or CI system retains. Put the conditional in a framework lifecycle hook that runs after the test and before page/context disposal.

  1. Run the test and let the framework record its outcome.
  2. In teardown or cleanup, determine whether the test failed or errored using that framework’s result API.
  3. If it did, create the artifact directory, construct a collision-safe filename, and await Page.ScreenshotAsync.
  4. Keep the file in the workspace or attach/upload it using the runner or CI system’s own artifact mechanism.

Playwright .NET provides runner integrations and base classes for NUnit, MSTest, xUnit, and xUnit v3, with per-test page/context lifecycle support. For custom test infrastructure, manage those lifetimes yourself and perform the capture at the equivalent finalization point. See the Playwright .NET library guide and installation documentation.

Save a screenshot to a file

This framework-neutral pattern shows the essential operation. It is intentionally not a drop-in teardown for any one test framework: replace testFailed, testName, and runId with values from your runner and job. Keep it inside the teardown hook while Page is valid.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
using System.IO;
using System.Threading.Tasks;
using Microsoft.Playwright;

public static async Task SaveFailureScreenshotAsync(
    IPage page,
    bool testFailed,
    string testName,
    string runId)
{
    if (!testFailed)
        return;

    Directory.CreateDirectory("artifacts");

    var safeTestName = MakeFileSystemSafe(testName);
    var path = Path.Combine(
        "artifacts",
        $"{safeTestName}-{runId}.png");

    await page.ScreenshotAsync(new PageScreenshotOptions
    {
        Path = path
    });
}

// Supply a real implementation that replaces invalid filename characters
// and handles names that become empty or too long.
static string MakeFileSystemSafe(string name)
{
    foreach (var c in Path.GetInvalidFileNameChars())
        name = name.Replace(c, '_');

    return string.IsNullOrWhiteSpace(name) ? "test" : name;
}

Use the current framework’s outcome object to set testFailed. For example, Playwright’s NUnit trace example checks TestContext.CurrentContext.Result.Outcome in teardown; the status and attachment APIs differ across runners. Do not treat the placeholder variables above as framework APIs. The official Trace Viewer guidance includes the NUnit-specific pattern.

A test name alone may not be unique when parameterized tests, retries, repeated runs, or parallel workers are involved. Include enough identity—such as a run/job identifier, worker identity, and test case identity—to avoid overwriting another failure’s evidence. This is an implementation safeguard: files written to the same path can collide. Playwright documents worker-based test execution in its running and debugging tests guide.

Capture bytes for runner attachments or CI uploads

If your runner or CI provider accepts an in-memory attachment, omit Path. Playwright returns the screenshot as a byte array; publishing it is a separate operation that you must perform with the runner or CI API.

byte[] image = await Page.ScreenshotAsync();

// Pass image to your framework's attachment API or CI upload client.
// The attachment method depends on the runner and CI provider.

Choose one storage route deliberately. A path is straightforward when the job collects files from a known artifacts directory. A byte array is useful when the test framework exposes an attachment method or the CI client uploads streams directly. Either way, confirm that the job retains the file or attachment after the test process exits; Playwright’s screenshot API does not upload or preserve CI artifacts by itself. The screenshots guide and Page API reference document the file and returned-byte behavior.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose what the image should show

A screenshot can capture different scopes. Select the smallest scope that will answer the debugging question.

Capture When it helps Example
Current viewport Inspect the page as it appeared at the point of failure. await Page.ScreenshotAsync(new PageScreenshotOptions { Path = path });
Full scrollable page Find content outside the viewport, including lower-page layout or lazy-loaded content. await Page.ScreenshotAsync(new PageScreenshotOptions { Path = path, FullPage = true });
One element Focus on a control, component, or region implicated by the assertion. await Page.Locator(".checkout-summary").ScreenshotAsync(new LocatorScreenshotOptions { Path = path });

Use a selector that identifies the intended element reliably; a missing or ambiguous target can make a focused capture fail or capture the wrong part of the page. Screenshot options also include image format (PNG, JPEG, or WebP), quality where applicable, scale, timeout, and styling controls. The documented default screenshot timeout is 30,000 milliseconds (30 seconds). See the Page API reference for the options supported by the version you use.

Put the outcome check in the right runner hook

Use the Playwright integration for your test framework where practical. Its lifecycle support creates the page/context for a test and provides hooks at the right point to inspect the result and capture evidence. The concrete outcome property, teardown signature, and attachment call are framework-specific, so consult that runner’s integration example rather than copying another framework’s property name.

  • NUnit: use the teardown lifecycle and inspect NUnit’s current test result before the page is disposed.
  • MSTest: use the corresponding test cleanup lifecycle and the test context/result facilities available in your version.
  • xUnit or xUnit v3: use the Playwright-provided base-class lifecycle or a fixture/disposal arrangement that still has access to the test result and live page.
  • Custom runner: retain the page until the outcome is known, then invoke the screenshot method before closing the page or context.

In all cases, make screenshot capture best-effort if your runner must preserve the original failure as the primary result. A screenshot timeout, invalid output path, or already-closed page can itself throw; handle or report that secondary artifact error without masking the test’s actual assertion failure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Screenshot or trace: which failure evidence should you keep?

A screenshot is a still image. It can quickly reveal a wrong route, broken layout, missing element, or unexpected visual state, but it does not show the sequence that led there. A trace records a timeline and is more useful when the failure depends on actions, navigation, or state changes.

Evidence What it provides Use it when
Screenshot One page or element state as an image. You need a quick visual artifact that is simple to open or attach.
Trace Action sequence, page snapshots, screenshots, errors, and logs in Trace Viewer. You need to reconstruct what happened before the failure.

The Playwright .NET Trace Viewer guide demonstrates starting tracing and saving a trace on failure. You can keep both artifacts when a still image is useful for triage but action history is needed for diagnosis. There is an important API distinction: lower-level BrowserContext.Tracing does not record test assertions. Playwright recommends runner-aware tracing when assertion information and a more complete test trace matter; see the Tracing API reference.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Run reliably in parallel and CI

Prevent files from overwriting one another

Parallel workers can fail tests at the same time. A constant filename such as failure.png makes one capture replace another. Build filenames from stable test identity plus run or worker identity where necessary, and sanitize test names for the filesystem. Keep artifacts in a job-specific directory when multiple jobs share storage.

Preserve the original test failure

Teardown runs after a failure, so artifact code should not turn a useful assertion error into a less informative screenshot error. Catch expected artifact exceptions at the boundary where you report attachments, and include the capture error in logs. Avoid silently swallowing errors if the screenshot is required by your debugging policy.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep retention separate from capture

Playwright creates the image; your runner or CI configuration determines where it is uploaded, how long it remains available, and who can access it. Ensure the path used by the screenshot matches the directory your job collects. For byte-based attachments, use the provider’s supported attachment/upload mechanism.

Troubleshoot common failures

  • No screenshot appears after a failed test: verify that the cleanup hook ran, the result condition classifies the outcome as failure/error, and capture happens before page disposal. Check whether the artifact directory is included in the job’s upload step.
  • The screenshot call reports a closed page or context: move it earlier in teardown. Do not close the page/context before the conditional capture.
  • One failure image replaces another: remove fixed filenames and include test, run, and where needed worker identity.
  • The call times out: inspect the page state and increase the screenshot timeout only if capture genuinely needs longer. The Page API’s documented default is 30 seconds; the option is configurable.
  • The file is missing even though capture succeeded: check the process working directory, relative path, directory creation, permissions, and the CI artifact collection path.
  • The screenshot does not explain an intermittent failure: add failure-only tracing; a still image cannot reveal preceding actions or assertion history.
  • An element capture cannot find its target: verify the locator and capture timing, or capture the page instead to retain broader context.

Or skip the browser setup

If you need a screenshot from a URL rather than the live state of a failing Playwright test, ScreenshotNeo offers a one-request screenshot API. It cannot capture your test’s in-memory page state; use the teardown method above for that. For an independent URL capture, one GET request returns an image or PDF:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for request options. ScreenshotNeo removes cookie/consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Its MCP server lets AI agents use screenshot tools. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Visit ScreenshotNeo or sign up free for 1,000 screenshots a month, no card required.

Frequently asked questions

Does Playwright automatically take a screenshot when an assertion fails?

No. Your test runner’s hook checks the result and calls the screenshot API; the screenshot method itself has no failure-only condition.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can I use the same approach outside NUnit, MSTest, or xUnit?

Yes. Manage the page/context lifetime yourself and call the same screenshot API from the point where your runner makes the final test outcome available, before disposing the page.

Can an external screenshot API capture the exact failed test state?

Not from a URL alone. Use Playwright’s page screenshot while the test page is alive to preserve that browser state; a URL screenshot service captures a separate visit.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.