Visual Regression Testing Tutorial
A free, hands-on visual regression testing course: screenshot baselines with Playwright, stable screenshots, diff thresholds, responsive and cross-browser baselines, CI review workflows and a cloud grid.
What you will learn
- Visual Regression Testing Tutorial
- Your First Screenshot Comparison with Playwright
- Making Visual Tests Stable
- Thresholds: How Much Difference to Allow
- Full Page, Element and Component Snapshots
- Cross-Browser Visual Testing
- Visual Testing on a Cloud Grid with TestingBot
- Visual Regression Testing in CI with GitHub Actions
- A Visual Testing Strategy That Lasts
- Visual Regression Testing Quiz
On this page
Last reviewed November 2026 against @playwright/test 1.65.0.
Visual regression testing compares what a page looks like now with an approved picture of what it looked like before. The approved picture is called the baseline (or golden image). Each test run takes a fresh screenshot, compares it pixel by pixel with the baseline and fails when too much has changed. A person then looks at the difference and decides: bug, or intended change that becomes the new baseline.
That last step is the whole point. A visual test does not know what "correct" looks like. It only knows what you approved last time, and it makes sure nothing changes without somebody noticing.
What functional tests miss#
A functional test asserts on the DOM: this text is present, that button is enabled, the URL changed. All of those can be true while the page is visibly broken. Some examples teams run into:
- A CSS change for one component leaks into another through a shared class, and a heading now overlaps the form below it.
- A web font fails to load and the fallback font pushes the "Pay" button below the fold on small screens.
- A
z-indexchange puts a cookie banner over the checkout button. The button is still in the DOM, still enabled, andtoBeVisible()still passes. - A dependency upgrade changes default margins, and every card on the page shifts a few pixels.
- Dark mode ships with dark grey text on a dark grey background.
Here is the smallest version of that problem, on Playwright's TodoMVC demo. The test adds two items and checks their text:
await expect(page.getByTestId('todo-title')).toHaveText(['Buy milk', 'Pay rent']);
Now inject a one-line CSS regression, .todo-list li label { padding-left: 70px; }, which pushes the labels 10 pixels to the right. The text assertion still passes, because the text is still there. A screenshot assertion on the same page fails with 719 pixels (ratio 0.01 of all image pixels) are different and writes a diff image with the changed pixels highlighted in red. You will reproduce exactly this in chapter 1.
What it costs#
Visual tests are cheap to write (often one line) and expensive to own. Before you add hundreds of them, know where the cost is:
- Baselines are files you maintain. Every intended design change fails the tests that cover it, and somebody has to review and approve new images. A redesign can mean updating dozens at once.
- Screenshots depend on the environment. The same page rendered by the same Chromium version differs between macOS and Linux, because fonts and anti-aliasing differ. When we compared macOS baselines with screenshots from Linux for the course pages, every test failed, with 1,959 to 3,541 pixels different. Baselines must come from the same environment the tests run in (chapters 2 and 7).
- Dynamic content causes false failures. Clocks, random recommendations, ads, animations and blinking cursors change between runs. Unless you freeze or mask them, the suite fails for no reason and people stop trusting it (chapter 2).
-
Tolerances are a trade-off. Too strict and anti-aliasing noise fails builds; too loose and real regressions pass. In chapter 3 a brand colour change from
#1a73e8to#1e88e5passes Playwright's default settings, while a one-pixel text shift fails them.
None of these are reasons to skip visual testing. They are reasons to start small, on the pages where a visual bug costs real money, and to set the suite up so failures are rare and meaningful.
A cookie banner now covers the checkout button. The functional test still clicks through checkout. Why did it not catch the problem?
DOM assertions see an element that exists and is enabled; they do not see what is painted on top of it. A screenshot comparison sees the pixels the user sees, which is the gap visual tests fill. Timeouts have nothing to do with it.
Visual testing and visual regression testing#
People use the two terms almost interchangeably. Strictly, visual testing is any check of how a UI looks, including a human looking at it. Visual regression testing is the automated kind this course teaches: compare against an approved baseline, flag what changed. The "regression" part matters, because the technique finds changes, not bugs. A page that has looked wrong since the day its baseline was approved will pass every run.
The tools in this course#
The course uses Playwright Test's built-in toHaveScreenshot() assertion, in TypeScript. It is free, lives in the same runner as your functional tests, stores baselines as PNG files in your repository, and produces the expected, actual and diff images in its HTML report. The ideas carry over to any tool: baselines, stabilisation, thresholds, per-environment baselines and a review workflow exist in every visual testing product.
In chapter 6 you run the same suite on remote browsers on the TestingBot grid and look at TestingBot's own visual testing features, which keep baselines on the server and give you a review screen instead of PNG files in git.
If you work in Python, our Python visual testing article covers the same ideas with pytest. For a shorter Playwright-only walkthrough, see the Playwright visual regression testing article and the Playwright visual regression docs.
How the course is organised#
- Your first screenshot comparison: baselines, failures, diff images and updating
- Making screenshots stable: animations, clocks, random content, fonts and operating systems
- Thresholds: what
threshold,maxDiffPixelsandmaxDiffPixelRatioreally do - Full page, element and component snapshots, and responsive breakpoints
- Cross-browser visual testing with one baseline per browser
- Visual testing on a cloud grid, and TestingBot's visual comparison features
- Visual tests in GitHub Actions: Linux baselines, updates through pull requests, artifacts
- Strategy: what to snapshot, who approves, and when not to use a visual test
- A knowledge check
Every example runs against public demo pages or HTML that the test creates itself, so you can copy them and see them pass. You should know basic Playwright (writing a test, locators, npx playwright test); if you do not, our Playwright tutorial gets you there first.
A note on certification: the course is free and needs no account, and the knowledge check at the end is a self-check. If you want something to show an employer, the optional TestingBot certification asks for two things: a passed exam and a visual suite running green on TestingBot in at least two browser configurations. It is issued by TestingBot, and the certificate says so.