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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Build the filename from pytest’s test metadata, add a stable case or run identifier, sanitize the result, and save it with Selenium’s .save_screenshot(). A practical pattern is test_name__case_id__run_id.png. Selenium returns True when the file is written and False for an I/O failure, so production test code should check that result.

Choose a filename pattern first

Decide what must be searchable in CI artifacts before writing code. Keep the test portion stable, make parameter or case information explicit, and add a run component whenever the same test can produce more than one image.

Component Example Purpose
Test name test_checkout_card_declined Identifies the scenario.
Case ID visa_4000 Distinguishes a data-driven case.
Run, retry, or worker ID run_20260929_w2_r1 Prevents parallel jobs overwriting one another.
Extension .png Required by Selenium’s PNG screenshot methods.

For example: test_checkout_card_declined__visa_4000__w2_r1.png. A filename alone does not guarantee uniqueness: different values can collapse to the same sanitized text, so include a short unique suffix when collisions matter.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

Direct Selenium capture in a pytest test

Use this approach when the test decides exactly when to capture, or when pytest-selenium is not responsible for debug artifacts.

import re
from pathlib import Path
from uuid import uuid4

SCREENSHOT_DIR = Path("screenshots")


def safe_stem(value: str) -> str:
    """Return a filesystem-friendly, bounded filename stem."""
    value = re.sub(r"[^A-Za-z0-9._-]+", "_", value)
    value = value.strip("._-")
    return value[:160] or "test"


def screenshot_name(test_name: str, case_id: str | None = None,
                    run_id: str | None = None) -> str:
    parts = [test_name]
    if case_id:
        parts.append(case_id)
    if run_id:
        parts.append(run_id)
    return f"{safe_stem('__'.join(parts))}.png"


def save_named_screenshot(driver, test_name: str,
                          case_id: str | None = None) -> Path:
    SCREENSHOT_DIR.mkdir(parents=True, exist_ok=True)
    run_id = uuid4().hex[:10]
    path = SCREENSHOT_DIR / screenshot_name(test_name, case_id, run_id)
    if not driver.save_screenshot(str(path)):
        raise OSError(f"Selenium could not write screenshot: {path}")
    return path


def test_declined_card(driver):
    # ...navigate, fill the form, and assert the expected result...
    path = save_named_screenshot(driver, "test_checkout_card_declined", "visa_4000")
    print(f"Saved {path}")

save_screenshot() captures the current browser window. Pass a full path when possible, create the directory first, and retain the .png suffix. Selenium’s alternative get_screenshot_as_file(filename) follows the same basic file-writing model.

Use pytest metadata for the real test name and ID

Inside a pytest fixture or hook, the test item is the authoritative source for the collected test name. item.name is the name used by pytest-selenium’s documented debug-capture example. For parameterized tests, inspect the value produced by your installed pytest version rather than assuming every plugin exposes the parameter ID in the same field.

import re
from pathlib import Path


def safe_stem(value: str) -> str:
    value = re.sub(r"[^A-Za-z0-9._-]+", "_", value).strip("._-")
    return value[:160] or "test"


def save_for_item(driver, item, case_id=None, worker_id=None):
    parts = [item.name]
    if case_id:
        parts.append(str(case_id))
    if worker_id:
        parts.append(str(worker_id))

    directory = Path("screenshots")
    directory.mkdir(parents=True, exist_ok=True)
    path = directory / f"{safe_stem('__'.join(parts))}.png"

    if not driver.save_screenshot(str(path)):
        raise OSError(f"Screenshot write failed: {path}")
    return path

Do not use pytest’s node metadata blindly in a standalone Selenium script: that script has no pytest item unless you pass one through a fixture or hook.

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

Automatically name pytest-selenium debug screenshots

If your project already uses pytest-selenium, implement its pytest_selenium_capture_debug(item, report, extra) hook. The hook receives the test item and a list of debug entries. Find the entry named Screenshot, decode its base64 content, and write the bytes with a sanitized item name.

import base64
import re
from pathlib import Path

SCREENSHOT_DIR = Path("screenshots")


def safe_stem(value: str) -> str:
    value = re.sub(r"[^A-Za-z0-9._-]+", "_", value).strip("._-")
    return value[:160] or "test"


def pytest_selenium_capture_debug(item, report, extra):
    for entry in extra:
        if entry["name"] != "Screenshot":
            continue

        SCREENSHOT_DIR.mkdir(parents=True, exist_ok=True)
        image = base64.b64decode(entry["content"].encode("utf-8"))
        filename = f"{safe_stem(item.name)}.png"
        (SCREENSHOT_DIR / filename).write_bytes(image)

This is an adapted version of the project guide’s example: sanitization, directory creation, and a length limit make it safer for varied test names. The guide’s documented code uses item.name + ".png". If parameter IDs are essential, print or inspect the collected item representation in your environment and add the verified ID explicitly; do not promise a particular field without checking your pytest and plugin versions.

When to use the hook, direct API, or a plugin

Method Best fit Control over naming Important consideration
Direct Selenium API The test chooses the capture moment. Highest; construct any path you need. Check the Boolean return and manage directories.
pytest-selenium hook pytest-selenium already collects failure artifacts. High; use item.name and your own suffix logic. Debug entries are base64 payloads; always-capture reports can become very large.
pytest-screenshot-on-failure You want a package-managed failure workflow. Depends on its options and behavior. Its PyPI page lists version 1.0.0 released July 21, 2023; verify current compatibility, maintenance, and security before adoption.

pytest-selenium’s selenium_capture_debug setting supports never, failure (the documented default), and always. The guide cautions that always collecting debug data can dramatically increase report size. A custom hook is often simpler when the only requirement is a predictable filename.

Include IDs safely in parameterized tests

IDs can contain spaces, slashes, punctuation, or control characters. Treat every test-derived value as untrusted path text. Replace runs of unsupported characters, trim leading and trailing dots or dashes, cap the total length, and retain a fallback such as test if the result is empty.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Use separators such as double underscores so the test, case, and run portions remain easy to search.
  • Never allow a raw slash or backslash to become a subdirectory unexpectedly.
  • Reserve a unique worker, retry, timestamp, or UUID component for repeated captures.
  • Keep screenshot directories outside source-controlled code unless the images are intentional fixtures.

For especially long parameter values, hash the value and retain a short readable prefix. Store the complete case data in the test report or CI metadata rather than creating an unwieldy path.

Troubleshooting filename and capture failures

The screenshot file is missing

Confirm that the destination directory exists and that the process can write there. Use an absolute path while diagnosing, print the resolved path, and test the Boolean returned by save_screenshot(). Selenium’s implementation catches operating-system errors and reports failure through that return value.

The name contains strange characters

Apply the sanitizer before constructing Path. This is especially important for parameter IDs and names supplied by external data. Avoid reserved names and trailing dots on Windows, and keep the path comfortably below operating-system path limits.

Two screenshots overwrite each other

Add a retry, worker, or unique run component. Sanitization can make distinct inputs identical, and parallel workers writing the same path have no automatic collision protection.

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

The hook never runs

Ensure the function is in a discovered conftest.py, pytest-selenium is installed and active, and the test actually produces a debug entry named Screenshot. If you do not use pytest-selenium’s capture flow, call Selenium’s API directly instead.

The report is enormous

Use selenium_capture_debug = failure or never rather than always, and keep only the artifacts needed for diagnosis. Full debug capture can include screenshots, HTML, URL data, and logs.

The image shows the wrong state

Capture after navigation, waits, assertions, and any required clicks. A correctly named file can still represent an intermediate page if the browser has not finished rendering.

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

Or skip the browser setup

For server-side captures, ScreenshotNeo accepts a URL and returns an image or PDF. Its cleanup step accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

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

Use the same URL in a CI job and name the downloaded response with your test ID:

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 documentation for authentication and options. The service supports PNG, JPEG, WebP, and PDF, with full-page capture, lazy-image loading, CSS-selector element capture, device presets, custom viewport and retina scale, JavaScript and CSS, waits, request blocking, headers and cookies, timezone and geolocation, transparent backgrounds, resizing, caching, signed links, asynchronous webhooks, bulk capture, usage reporting, and an OpenAPI specification.

There is a free allowance of 1,000 screenshots per month without a card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account to try it.

Python, Node.js, and API equivalents

Python

import requests

r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

When integrating an API capture with test artifacts, derive the output filename with the same sanitizer used for Selenium and preserve the response headers alongside the image so billing and verdict status remain auditable.

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

Frequently Asked Questions

Does Selenium capture the entire page with save_screenshot() automatically?

The documented method captures the current browser window. Full-page behavior depends on the driver and browser; use a page-capture capability or a service designed for full-page images when the viewport is insufficient.

What extension should a Selenium Python screenshot use?

Use .png with save_screenshot() or get_screenshot_as_file(); Selenium’s API documentation advises a PNG filename.

Can I use a test ID without pytest-selenium?

Yes. Pass the ID from your fixture or test data to a filename-building function, sanitize it, and append it before calling Selenium’s screenshot method.

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.

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