What visual regression testing is
Visual regression testing takes a screenshot of a page, a component or a screen, and compares it with a baseline: the same screenshot, taken earlier, that someone looked at and approved. If the two differ by more than a threshold, the test fails and the tool keeps the new screenshot and a diff image that highlights what changed. A human then decides: a bug, or a change that should become the new baseline.
That is the whole idea. Everything else, the tools, the hosted services, the arguments about flakiness, hangs off these three files per screenshot: baseline, actual, diff.
What it catches that other tests miss
A unit test checks that a function returns the right value. An end-to-end test checks that clicking "Buy" produces an order. Neither notices that the "Buy" button is now rendered white on a white background, that a CSS refactor moved the footer under the sidebar, or that a font update made every heading wrap onto two lines. Assertions see the DOM; users see pixels.
Visual regression tests are the only automated check that looks at what the user looks at. They are cheap to write (one line per screenshot), broad (one screenshot covers everything in the viewport), and they catch the class of bugs that is otherwise found by a designer, a customer, or nobody.
What it is not
- Not a replacement for functional tests. A screenshot cannot tell you a form submits. Use both.
- Not an exact-match check. Browsers render text and anti-aliasing slightly differently between versions and machines. Every serious tool allows a tolerance, and chapter 3 is about keeping the noise below it.
- Not "snapshot testing" of markup. Jest-style snapshots serialise a component tree to text. They catch changes in markup, not in rendering. The two are often confused because some libraries do both.
- Not design review. A baseline is what the app looked like last time, not what the design says it should look like. The test catches unintended change; whether the intended change is right is still a conversation.
The vocabulary
| term | meaning |
|---|---|
| baseline (reference, golden, expected) | the approved screenshot a new one is compared against |
| actual (current, received, test) | the screenshot taken in this run |
| diff | an image highlighting the pixels that differ |
| threshold | how much difference is tolerated before the test fails; per pixel (colour distance) and per image (share of pixels) |
| approve (accept, update) | make the actual image the new baseline |
| flaky | a comparison that fails without any change to the code |
Tools disagree on the words but not on the concepts; the manifest uses baseline, actual and diff.
Where it fits
Teams usually start with the pages or components that break most and that nobody notices breaking: marketing pages after a redesign, a design-system's component library, checkout flows, anything with careful typography. One screenshot per component story (Storybook makes this almost free) and one per key page is a common first setup, and it already catches most layout regressions.
From there the questions become operational: where do baselines live, who approves changes, how do you keep the comparisons stable across machines, and what happens in CI. The next chapters take them in that order.
Further reading
- Regression testing on Wikipedia, for where the term comes from.
- The Test Pyramid by Martin Fowler, on why broad-but-slow tests should be few.
- Visual tests in the Storybook docs, the component-level view.
- Screenshots in the Playwright docs, the page-level view.