Frameworks3 min read

Selenium visual testing

Selenium captures screenshots but has no comparison layer. The options for adding visual regression testing to an existing Selenium suite — commercial SDKs, self-built comparison, and when migrating is the better answer.

VisualMax EngineeringProduct team

Selenium has getScreenshotAs() and nothing else. There is no baseline, no comparison, no approval workflow — the WebDriver specification covers capturing a screenshot and stops there. Everything beyond that is a library you add or a service you subscribe to.

Most teams reading this have a large existing Selenium suite and want visual coverage without rewriting it. That constraint shapes the answer, so it is worth taking seriously rather than being told to migrate.

Your three options

ApproachEffortCostReview workflow
Commercial SDKLow — a call per assertionPaidHosted UI included
Self-built comparisonHighFreeYou build it
Visual testing outside the suiteNone in the suitePaidHosted UI included

Option 1: a commercial SDK

The lowest-friction path for an existing suite. You add a dependency and an assertion; the vendor handles comparison, baselines, and review.

  • Applitools — the deepest Selenium SDK support in the category, with perceptual comparison that suppresses antialiasing noise. Starter is $667/mo billed annually, for 100,000 component checkpoints or 1,000 page checkpoints.
  • SmartUI (TestMu AI) — supports Selenium alongside Cypress, Playwright, Puppeteer, Jest, Appium, XCUITest and Espresso. Free plan is 2,000 screenshots; paid pricing is not published. Note the rebrand from LambdaTest in January 2026.
  • Percy — lists Selenium among its automation-framework integrations, with a free tier of 5,000 screenshots per month. Paid tiers are contact-sales, and the dedicated pricing page returned a 404 when checked.
java
// Applitools Eyes with Selenium, Java
Eyes eyes = new Eyes();
eyes.setApiKey(System.getenv("APPLITOOLS_API_KEY"));

try {
    eyes.open(driver, "Acme", "Pricing page", new RectangleSize(1280, 800));
    driver.get("https://example.com/pricing");
    eyes.checkWindow("Pricing");
    eyes.close();
} finally {
    eyes.abortIfNotClosed();
    driver.quit();
}

Option 2: build the comparison yourself

Viable, free, and more work than it first appears. Selenium gives you the bytes; you supply everything else.

java
// Capture, then compare against a stored baseline.
byte[] current = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
Path baseline = Path.of("visual/baseline", name + ".png");

if (!Files.exists(baseline)) {
    Files.createDirectories(baseline.getParent());
    Files.write(baseline, current);
    return; // recorded
}

BufferedImage expected = ImageIO.read(baseline.toFile());
BufferedImage actual = ImageIO.read(new ByteArrayInputStream(current));

// A dimension change is a difference, not an error to throw on.
if (expected.getWidth() != actual.getWidth() || expected.getHeight() != actual.getHeight()) {
    fail("Page dimensions changed for " + name);
}
// ...then a pixel walk, or hand off to a comparison library.

On the JVM, image-comparison (Java) is the usual comparison library. In Node-based Selenium suites, pixelmatch is the standard choice — the mechanics are the same as in the Puppeteer guide, which walks through a complete implementation including the size-mismatch trap.

What you are signing up to maintain: baseline storage, branch-aware comparison, a report someone can read, and — the expensive one — determinism.

The Selenium-specific determinism problem

Selenium is harder to stabilise for visual work than the modern alternatives, for structural reasons rather than quality ones.

  • No native network interception. Playwright and Cypress can stub a response in one line. In Selenium you need a proxy such as BrowserMob, or a test environment with seeded data.
  • No built-in clock control. Freezing time means injecting JavaScript through executeScript before the application initialises, which is awkward to sequence reliably.
  • Waiting is explicit. WebDriverWait waits for elements, not for the page to have finished becoming visually stable — fonts and late paints are not covered.
  • No retry-until-stable assertion. Nothing equivalent to Playwright's toHaveScreenshot(), which re-captures until the image settles before comparing.
java
// The minimum stabilisation before capturing.
JavascriptExecutor js = (JavascriptExecutor) driver;

js.executeScript(
    "const s = document.createElement('style');" +
    "s.textContent = '*,*::before,*::after{animation:none!important;" +
    "transition:none!important;caret-color:transparent!important}';" +
    "document.head.appendChild(s);");

new WebDriverWait(driver, Duration.ofSeconds(10)).until(
    d -> Boolean.TRUE.equals(((JavascriptExecutor) d)
        .executeScript("return document.fonts.status === 'loaded'")));

Font readiness is the single highest-value check here — it removes more false failures than any threshold adjustment. The rest of the causes are covered in how to fix flaky visual tests.

Option 3: leave the suite alone

Worth stating plainly because it is often the right answer and rarely the one offered: visual coverage does not have to live inside your functional tests. A service that drives the application independently gives you visual checks without touching a large, working Selenium suite — no SDK to thread through hundreds of tests, no comparison layer to maintain, and no risk of destabilising tests that currently pass.

The trade-off is that it is a separate system with its own bill, and it will not reuse the page objects and flows you have already modelled.

Should you migrate off Selenium?

Not for this alone. A large Selenium suite represents years of encoded domain knowledge, and rewriting it to get better screenshot ergonomics is a poor trade. Selenium's cross-language support and Grid infrastructure remain genuinely good reasons to stay.

If you are choosing fresh, Playwright is the stronger foundation for visual work — comparison is built in, the assertion retries until stable, and Chromium, Firefox and WebKit come as projects rather than as infrastructure. Full comparison of tools in 10 visual regression testing tools, compared.

Frequently asked questions

Does Selenium support visual testing?

Only partially. Selenium's getScreenshotAs() captures an image, but WebDriver has no baseline, comparison, or approval concept. Visual regression testing with Selenium requires either a commercial SDK such as Applitools or SmartUI, or a comparison layer you build yourself.

How do you compare screenshots in Selenium?

Capture with getScreenshotAs(OutputType.BYTES), then compare against a stored baseline using an image comparison library — image-comparison on the JVM, or pixelmatch in Node-based suites. Handle dimension changes explicitly, and stabilise the page first by disabling animation and waiting for document.fonts.status to be loaded.

Is Playwright better than Selenium for visual testing?

For visual testing specifically, yes. Playwright ships comparison built in, retries until the image is stable before diffing, intercepts network requests natively, and controls the clock — all things Selenium requires workarounds for. That is not a reason to rewrite a large existing Selenium suite, but it is the better foundation for a new one.

Sources

  1. Applitools pricingchecked 2026-09-13
  2. SmartUI by TestMu AIchecked 2026-09-13
  3. Percy by BrowserStack product pagechecked 2026-09-13

Referenced by