This guide shows you how to use Selenium WebDriver to capture screenshots of your web pages and compare them automatically to catch unintended visual changes. You’ll learn to set up a simple pipeline that takes baseline screenshots, re-takes them after code changes, and highlights differences using Python and a few well-known libraries.
What You’ll Need
- Python 3.7+ installed on your system
- A browser driver (ChromeDriver, GeckoDriver for Firefox, or EdgeDriver) matching your browser version
- Basic knowledge of Python and Selenium
- Pip to install packages
Step-by-Step
- Install required packages
Open your terminal and run:
pip install selenium pillow pytest
Pillow is used for image comparison, pytest for organizing tests. You can also use
pytest-seleniumif you prefer, but plain Selenium is enough. - Download a browser driver
Go to the ChromeDriver download page (or equivalent for your browser), download the version that matches your installed browser, and place the executable in a known folder. Add that folder to your system PATH, or pass the path directly in your script.
- Create a base screenshot function
Write a function that opens a URL, waits for the page to load, and saves a screenshot. Use explicit waits to ensure all dynamic content is rendered before capturing.
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC def take_screenshot(url, filename, width=1280, height=720): options = Options() options.add_argument(f"--window-size={width},{height}") driver = webdriver.Chrome(options=options) try: driver.get(url) WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.TAG_NAME, "body")) ) driver.save_screenshot(filename) finally: driver.quit() - Capture baseline screenshots
Run the function on the current version of your site and save the images in a folder named
baseline/. These are your reference images.take_screenshot("https://example.com", "baseline/home.png") - Write a comparison function
Use Pillow to compare the current screenshot with the baseline. Compute the mean squared error (MSE) or count differing pixels. A threshold determines if the difference is acceptable.
from PIL import Image, ImageChops import numpy as np def compare_images(baseline_path, current_path, threshold=0.01): baseline = Image.open(baseline_path).convert("RGB") current = Image.open(current_path).convert("RGB") diff = ImageChops.difference(baseline, current) np_diff = np.array(diff) mse = np.mean(np_diff ** 2) return mse < threshold, mse - Create a test that captures and compares
In a pytest file, write a test that takes a fresh screenshot, compares it to the baseline, and fails if the difference exceeds the threshold.
def test_homepage_visual(): take_screenshot("https://example.com", "current/home.png") passed, mse = compare_images("baseline/home.png", "current/home.png") assert passed, f"Visual difference detected (MSE={mse})" - Run the tests
Execute
pytestfrom your project root. If a test fails, inspect the current screenshot and the diff to identify the change. - Update baselines intentionally
When you make a deliberate design change, update the baseline by re-running the screenshot function and overwriting the baseline files. Commit these new baselines to version control.
Common Mistakes
- Not waiting for elements – Screenshots taken before animations or async content load will differ randomly. Always use explicit waits for key elements.
- Ignoring browser window size – Different window sizes produce different layouts. Fix the window size in your options, as shown above.
- Comparing images with different dimensions – If the page height changes, the screenshots will have different sizes. Resize or crop to a common region before comparison.
- Using a too-strict threshold – Anti-aliasing and font rendering can cause tiny pixel differences. Set a reasonable threshold (e.g., MSE < 10) instead of requiring exact equality.
Tips
- Use a dedicated test environment – Avoid running visual tests against production where dynamic content (ads, timers) will cause false positives.
- Store baselines in version control – This lets you track when and why visuals changed.
- Run tests in headless mode – Use
options.add_argument("--headless")to run without a visible browser, which is faster and works on CI servers. - Group related pages – Test multiple URLs in a single test using parametrize to avoid repetitive code.
- Generate a diff image – Save the diff image to a file so you can visually inspect what changed.
Troubleshooting
WebDriver not found – Ensure the driver executable is in PATH or pass its path to webdriver.Chrome(executable_path="/path/to/chromedriver").
Browser version mismatch – Check that the driver version matches your browser version. Update both if necessary.
Blank or partial screenshots – Increase the wait time or wait for a specific element that indicates the page is fully loaded.
Differences on every run – Look for dynamic content like dates, random ads, or carousels. Exclude those regions by cropping the image before comparison.
Slow tests – Use headless mode and consider parallelizing with pytest-xdist.
Frequently Asked Questions
Can I use this with Firefox or Edge?
Yes. Replace webdriver.Chrome() with webdriver.Firefox() or webdriver.Edge() and download the corresponding driver.
How do I ignore a specific part of the page?
Crop both images to the same region using Pillow's crop() method before comparison, or mask out the area.
What threshold should I use?
Start with MSE < 10 and adjust based on your site's variability. For pixel-count-based comparison, allow less than 0.1% of pixels to differ.
Can I run these tests in CI/CD?
Yes. Use headless mode and ensure the CI environment has the browser and driver installed. Store baselines in the repository.
How often should I update baselines?
Only when you intentionally change the design. For every code change, run the tests; if they fail, review the diff and decide if it's expected.
Is there a library that does this automatically?
Yes, libraries like pytest-selenium with selene or dedicated tools like Applitools or Percy offer more features, but the approach here is lightweight and free.
Related
General steps for current mainstream versions; menus can differ by device and version. See the disclaimer or report an error.
More in this bed
- Fix Shockwave Flash Crashes in Firefox
- Fix Microsoft Account Apps Notification in Windows 10
- Printer Problems With macOS Catalina: Fixes
- CSS min(), max(), clamp(): How to Use Them
- How to Partition an External Hard Drive
- How to Transfer PicsArt Stickers from Android to PC