Visual Regression Testing with DDEV

The best time to catch a visual regression is before it leaves your machine. With the Diffy add-on for DDEV, one command screenshots your local site at every page and breakpoint in your Diffy project and uploads the set to Diffy, where you compare it with production. You see what your branch changed before you push, open a pull request or deploy to staging.

The screenshots are taken inside your DDEV project, so your local site doesn’t need a public URL, a tunnel or a staging copy.

How it works

  1. The add-on adds a diffy service to your DDEV project: a container with a headless browser and the Diffy screenshot worker.
  2. When you run ddev screenshot, the worker fetches your Diffy project’s pages, breakpoints and screenshot settings from the Diffy API.
  3. It replaces the production domain in each page URL with your local site and captures every page at every breakpoint, applying the project’s login, masks and mocked content.
  4. It uploads the screenshots to Diffy as a new screenshot set named after your DDEV site, and prints a link to it.
  5. In Diffy, you compare that set with production, staging or a baseline and review the changes as usual.

What you need

  • DDEV v1.24.10 or later. Check with ddev version.
  • A Diffy project with your production URL, the pages you want tested and your breakpoints. If you’re choosing pages for the first time, see what visual regression testing covers.
  • A Diffy API key from the Keys page, and your project ID, the number in the project’s URL, as in app.diffy.website/#/projects/12345. API access comes with every paid plan.

Step 1: Install the add-on

In your project directory, run:

ddev add-on get DiffyWebsite/ddev-diffy
ddev restart

The installer downloads the Diffy worker into .ddev/diffy-worker, and the first start installs its npm dependencies, so you’ll see npm install output once. Commit the .ddev directory so the rest of your team gets the add-on too.

Step 2: Add your API key and project ID

Copy the example environment file and set your credentials:

cp .ddev/diffy-worker/.env.example .ddev/diffy-worker/.env
ddev dotenv set .ddev/diffy-worker/.env --diffy-api-key=YOUR_API_KEY --diffy-project-id=YOUR_PROJECT_ID

Keep .env out of version control: each developer can use their own API key.

Step 3: Take the screenshots

With your site running, run:

ddev screenshot

The worker captures each page at each breakpoint and reports its progress. When it finishes, it prints a link like https://app.diffy.website/#/snapshots/123456 to the uploaded set.

Step 4: Compare with production

Open the link, or go to your project’s screenshots list in Diffy. Use Compare Screenshots to compare the new set, named after your DDEV site (for example mysite.ddev.site), with your latest production set, or with a staging set or a baseline. Diffy builds the comparison on its servers and shows every page and breakpoint that changed.

A common rhythm is to take a fresh production set from Diffy, run ddev screenshot on your branch, and compare the two before you open a pull request.

Sites with several hostnames

By default, ddev screenshot loads your site at https://web, the web container’s address inside DDEV. If your project uses additional_hostnames in .ddev/config.yaml and you want to capture one of them, for example in a multisite, do two things.

Create .ddev/docker-compose.router.yaml so the diffy container can reach those hostnames:

services:
  diffy:
    external_links:
      - ddev-router:sitea.ddev.site
      - ddev-router:siteb.ddev.site

Then edit .ddev/commands/diffy/screenshot to point at the hostname you want. Remove the #ddev-generated line at the top first, so DDEV doesn’t overwrite your change:

node --env-file=.env diffy-screenshots --url=https://sitea.ddev.site --screenshot-name=sitea.ddev.site

Run ddev restart after both changes.

Reviewing the result

The comparison works like any other in Diffy. Changes are highlighted page by page and breakpoint by breakpoint, with a before and after slider, and the same change on many pages is grouped so you approve it once. Mark each change as expected or as a bug, and share a link with a teammate who doesn’t have a Diffy account if you want a second opinion.

When you’re happy, push your branch. If your pull requests get preview environments, the GitHub Actions or GitLab CI integration runs the same check again in CI.

FAQ

Does my local DDEV site need a public URL? No. The add-on runs a screenshot worker as a container inside your DDEV project, so it reaches your site over DDEV’s own network. Only the finished screenshots are uploaded to Diffy. There is no tunnel and nothing exposed to the internet.

Which pages and breakpoints does it capture? The ones in your Diffy project. The worker reads the project’s page list and breakpoints from Diffy, swaps the production domain for your local site, and applies the project’s screenshot settings, such as login, masking and mocked content.

Where do I see the screenshots? ddev screenshot prints a link to the uploaded screenshot set in Diffy, named after your DDEV site, for example mysite.ddev.site. From there you compare it with production, staging or a baseline like any other set.

Does it work with Lando or Docksal too? Yes. Diffy has equivalent integrations for Lando and Docksal, and a standalone Docker container for other setups. See the local development docs.

Which Diffy plan do I need? The worker authenticates with a Diffy API key, and API access comes with every paid plan. Paid plans also include development and custom environments, which local testing relies on.

Get started

Create a Diffy account, add your production URL and key pages, and run a first production screenshot set from the UI. Then install the add-on and run ddev screenshot. The add-on’s source is on GitHub, and all Diffy integrations are listed on the integrations page. Working on a Drupal site? See Drupal visual regression testing.