Python guide
Python visual regression testing with Playwright and Pillow
Build a simple visual regression test in Python: capture pages with Playwright, compare with Pillow, save a diff image and run it with pytest.
Python does not have a single standard visual testing library, but two well-known packages cover the essentials: Playwright for Python to capture consistent screenshots and Pillow to compare them. Together they make a small, understandable visual regression check.
Install the tools
Install Playwright, its browsers and Pillow into your test environment.
pip install playwright pillow pytest
playwright install chromiumCapture and compare
Capture a full-page screenshot with animations disabled, then compare it with the stored baseline. ImageChops.difference returns a black image where nothing changed; getbbox() gives the bounding box of anything that did.
from pathlib import Path
from PIL import Image, ImageChops
from playwright.sync_api import sync_playwright
BASELINE = Path("baselines/home.png")
CURRENT = Path("output/home.png")
def capture(url: str, path: Path) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 1280, "height": 800})
page.goto(url, wait_until="networkidle")
page.screenshot(path=str(path), full_page=True, animations="disabled")
browser.close()
def test_homepage_visual():
capture("https://example.com/", CURRENT)
if not BASELINE.exists():
BASELINE.parent.mkdir(parents=True, exist_ok=True)
CURRENT.replace(BASELINE)
return # first run creates the baseline
base = Image.open(BASELINE).convert("RGB")
current = Image.open(CURRENT).convert("RGB")
assert base.size == current.size, f"page size changed: {base.size} -> {current.size}"
diff = ImageChops.difference(base, current)
box = diff.getbbox()
if box:
diff.save("output/home-diff.png")
assert box is None, f"visual change detected in region {box}"Make it practical
Pixel-exact comparison is strict. Real suites add a tolerance (for example, counting changed pixels and failing above a percentage), mask regions that change by design, and keep baselines per viewport and browser.
- Allow a small changed-pixel ratio
- Paint over dynamic regions before diffing
- Store baselines per viewport
- Run in the same container locally and in CI
Scaling beyond a script
Once you need many pages, languages, scheduled runs and a shared review history, a script turns into a product to maintain. That is the point where teams usually adopt a dedicated tool.
Or skip the test code entirely
Code-based visual tests only cover the pages and states someone wrote a test for, and only run when the pipeline runs. VisualRunner captures production pages from a sitemap, crawl or URL list, across languages and viewports, and monitors them on a schedule — so CMS edits, translations and third-party scripts are caught too. Engineers can still trigger a monitor from CI through the API.
Questions
Frequently asked questions
What is the best Python library for visual regression testing?
There is no single standard. Playwright for Python (or Selenium) for capture plus Pillow or OpenCV for comparison is the most common do-it-yourself combination.
Can I use Selenium instead of Playwright?
Yes. Selenium can save screenshots with driver.save_screenshot(); the Pillow comparison step stays the same. Full-page capture is simpler in Playwright.
Continue exploring
Related resources
Turn the checklist into a repeatable workflow
Discover your pages, capture the important states and keep a visual history of every meaningful change.
Start free trial