Add visual regression checks inside the UI automation that runs your BDD scenarios: first drive the application to a meaningful, stable screen, then capture a named checkpoint and compare it with an approved baseline. Keep the scenario’s behavior assertions; the screenshot adds a check on how the interface looks rather than replacing what the scenario proves.
What visual testing adds to BDD
BDD scenarios describe concrete examples of behavior that teams can discuss and automate. Cucumber describes BDD as collaborative work that helps close the gap between business and technical people and creates shared understanding that is checked against behavior (Cucumber’s Behaviour-Driven Development documentation).
A visual check compares the rendered screen at a chosen point with a previously approved baseline. It can reveal a layout or rendering change that a text assertion or DOM check would not catch. It does not establish that the business behavior is correct: retain functional assertions for outcomes such as validation rules, submitted values, or navigation.
Where should visual assertions go in a Gherkin scenario?
Put the checkpoint in the automation after the scenario has reached a meaningful rendered state—not in every Gherkin step and not before the interface is ready. Good candidates include a completed sign-in, a form’s validation error, or a submitted form confirmation. The checkpoint should correspond to a screen whose appearance matters to users.
Keep the Gherkin scenario focused on behavior and shared language. The capture can live in the step-definition implementation, a page-object method, or a runner lifecycle hook, depending on the framework. Name the checkpoint after the screen or state so a failed comparison can be connected to the scenario that produced it.
How to add visual regression testing to existing BDD tests
- Choose a valuable state. Identify a user-visible outcome where a rendering regression would matter. Prefer a small number of meaningful checkpoints over screenshots at every action.
- Make the state repeatable. Use controlled test data and a consistent viewport. Wait for navigation and data loading to finish; ensure fonts have loaded and animations or transient content have settled. If part of the screen is expected to vary, use a narrowly scoped ignore or mask feature supported by your tool rather than suppressing broad areas of the page.
- Capture a named checkpoint. Give it a descriptive name, such as
Sign-in validation errororOrder confirmation. Include enough scenario context in your test output to identify which behavior reached that state. - Compare against an approved baseline. Treat a baseline as the reference for a defined application, environment, viewport, and state. A comparison is meaningful only when those conditions are sufficiently consistent.
- Review differences deliberately. Approve a new baseline when the visual change is intentional. Reject it when it reflects a defect, then investigate without replacing the known-good reference.
- Run the check with ordinary feedback. Execute visual checks alongside the UI tests locally or in CI. Make failures visible with the scenario name and checkpoint so someone can review the changed screen rather than merely seeing a generic test failure.
Playwright example with Applitools Eyes
Applitools documents a Playwright integration using its extended test fixture. In that pattern, the fixture provides both page and eyes, and eyes.check() captures a named checkpoint. The following is an illustrative test in that integration style; it is not a universal Cucumber or Playwright API:
import { test } from '@applitools/eyes-playwright/fixture';
test('shows a sign-in validation error', async ({ page, eyes }) => {
await page.goto('https://example.com/sign-in');
// Perform the scenario's actions and wait for the meaningful UI state.
await page.getByRole('button', { name: 'Sign in' }).click();
await page.getByText('Enter your email address').waitFor();
// Keep behavioral assertions when the exact content matters.
await eyes.check('Sign-in validation error', {
fully: true,
matchLevel: 'Strict'
});
});
The example uses an illustrative target and page text; replace those with selectors and expected behavior from your application. The documented fixture pattern supports options such as full-page capture, match level, and ignored regions. Its configuration also includes settings such as appName and whether visual differences fail the test. Check the current Applitools Playwright integration documentation for the package setup and options that match your installed versions.
Integrating with Cucumber or another BDD runner
The Playwright fixture example above should not be copied as if it were the setup for every BDD stack. Keep existing Gherkin scenarios and step definitions intact, then call the visual SDK at the layer that owns the rendered page and test lifecycle in your actual runner. For example, a step definition can invoke a page-object checkpoint method after a scenario action; a shared hook can initialize and close a visual-testing session when the vendor’s current integration calls for it.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallVendor APIs, packages, hooks, and lifecycle requirements vary by language and runner, so confirm them in the current documentation for the exact versions in use. Applitools has a Cucumber Ruby help article dated September 1, 2018 that illustrates placing shared setup in Cucumber support configuration. Its age makes it an architectural example, not reliable current installation instructions.
Choose comparison and review settings deliberately
Visual-testing approaches differ in how they compare screens and manage expected change. Choose based on the review workflow your team can maintain, and verify current capabilities in the tool’s documentation.
Rank #4
- Comparison method: pixel-level comparison and semantic or AI-assisted matching can behave differently when rendering changes. Understand what counts as a difference before making it a blocking test.
- Baseline location and approvals: decide whether references live with the test code or in a hosted review workflow, and define who may approve an intentional UI change.
- Coverage: decide whether the checkpoint needs one browser and viewport or broader browser and device coverage. More combinations also mean more baselines to maintain and review.
- Dynamic areas: identify timestamps, rotating content, user-specific data, or other intentional variation. Stabilize these inputs where possible; mask only the smallest region necessary when stabilization is not practical.
- Failure policy: determine how differences appear in local and CI runs and how a reviewer can see the changed image alongside the scenario and checkpoint name.
Troubleshooting visual-test failures
- The same screen fails inconsistently: the capture may be happening before data, fonts, or animations settle, or the test data and viewport may vary. Wait for a meaningful page condition, disable or finish animation where supported, and standardize inputs and viewport.
- A baseline changes for unrelated reasons: check whether the environment, browser, viewport, application data, or content changed. Keep the baseline tied to known capture conditions and do not approve differences until their cause is understood.
- Dynamic content creates noisy differences: make the content deterministic if possible. Otherwise, use a supported ignore or mask for only the unstable element, preserving checks on the surrounding layout.
- A visual check passes while behavior is wrong: screenshots do not replace assertions for business rules or dynamic values. Add or retain explicit functional checks for the outcome the scenario is meant to verify.
- The documented Playwright code does not fit the BDD runner: the fixture is specific to Applitools’ Playwright integration. Use the current vendor instructions for the language, runner, package version, and lifecycle in your project instead of transplanting fixture syntax into another stack.
- CI reports a difference that cannot be reviewed: include the scenario and checkpoint name in the test result and make the comparison output accessible to the reviewer through the chosen tool’s workflow.
Or skip the browser setup
If you need a screenshot from a URL without wiring screenshot capture into a browser test, ScreenshotNeo offers a screenshot API and MCP server. Its API accepts a URL in one GET request and returns an image or PDF; this is useful for capture tasks, but it does not replace an in-test visual assertion against an approved baseline.
For example, save a WebP capture of a page with cURL:
Recommended Free Tools
Best Value
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
See the ScreenshotNeo API documentation for parameters. Before a capture, ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each of these steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses include X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and other MCP clients.
The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for ScreenshotNeo’s free plan.
Quick Recap
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.




