How to Add Screenshot Testing with Cypress to Your Project

As front-end codebases grow in size and complexity, it becomes increasingly difficult to catch visual bugs and unintended changes. Even with a robust suite of functional tests, visual issues can easily slip through the cracks.

This is where visual regression testing comes in. By capturing screenshots of your application and comparing them against a baseline, you can automatically detect any visual changes and catch regressions before they make it to production.

In this post, we‘ll explore how to add screenshot testing to your project using Cypress, a popular end-to-end testing framework. With its powerful and easy-to-use API, Cypress makes it straightforward to set up visual testing alongside your existing functional tests.

We‘ll walk through the process step-by-step, from installing the necessary dependencies to configuring your screenshot tests and handling test failures. By the end, you‘ll have a solid understanding of how to incorporate screenshot testing into your own projects to catch visual bugs and ensure a high-quality user experience.

Let‘s get started!

Getting Started

Before we dive into the specifics of screenshot testing, let‘s make sure we have our project set up with Cypress.

If you haven‘t installed Cypress before, you can do so with npm:

npm install --save-dev cypress

Once installed, open up the Cypress Test Runner to make sure everything is working correctly:

npx cypress open  

This should launch the Cypress Test Runner, where you can see any existing test files and interactively run your tests.

With Cypress up and running, we can move on to installing the cypress-image-snapshot plugin, which adds screenshot testing capabilities to Cypress.

Again, we‘ll use npm to install the plugin as a dev dependency:

npm install --save-dev cypress-image-snapshot

Next, we need to configure Cypress to use the plugin. In your cypress/plugins/index.js file, add the following:

const { addMatchImageSnapshotPlugin } = require(‘cypress-image-snapshot/plugin‘);

module.exports = (on, config) => {
  addMatchImageSnapshotPlugin(on, config);
};

This loads the cypress-image-snapshot plugin and makes its functionality available within our Cypress tests.

We‘re now ready to start writing our first screenshot test!

Screenshot Testing with Cypress

Let‘s say we have a simple login page that we want to visually test. We can write a Cypress test that navigates to the login page, takes a screenshot, and compares it against a baseline:

describe(‘Login Page‘, () => {
  it(‘should match visual snapshot‘, () => {
    cy.visit(‘/login‘);
    cy.matchImageSnapshot();
  });
});

Here, we use cy.visit() to navigate to the login page URL. Then cy.matchImageSnapshot() captures a screenshot of the entire page and compares it against the baseline image.

The first time we run this test, it will fail because there is no baseline image to compare against. Cypress will automatically generate a baseline screenshot and store it in the cypress/snapshots directory.

On subsequent test runs, Cypress will compare the newly captured screenshot against the baseline. If there are any visual differences, the test will fail and output a diff image highlighting the changes.

We can also target specific elements on the page for screenshots. For example, let‘s say we want to visually test just the login form:

describe(‘Login Page‘, () => {
  it(‘login form should match visual snapshot‘, () => {
    cy.visit(‘/login‘);
    cy.get(‘#login-form‘).matchImageSnapshot();
  });
});  

Instead of capturing the full page, we use cy.get() to select the login form element and take a screenshot of just that specific element.

The cypress-image-snapshot plugin provides several options for customizing the screenshot behavior, such as setting a custom screenshot name, adjusting the image comparison threshold, and excluding dynamic content from the comparison. Check out the plugin documentation for more details.

Handling Test Failures

When a screenshot test fails, Cypress will output a diff image showing the differences between the baseline and the newly captured screenshot. These diff images are stored alongside the original screenshots in the cypress/snapshots directory.

Diff images are a useful debugging tool for understanding what changed and causing a test failure. They highlight any pixel differences in red, making it easy to spot visual regressions.

Of course, not all visual changes are bugs. Sometimes we intentionally update the design or UI of our application. In these cases, we need to update our baseline screenshots to reflect the new visuals.

To update a baseline screenshot, we can simply delete the existing baseline image. The next time we run the test, Cypress will treat it as a new test and capture a fresh baseline screenshot.

We can also use the cypress-image-snapshot plugin‘s updateSnapshots option to update all baseline images at once:

{
  "env": {
    "cypress-plugin-snapshots": {
      "updateSnapshots": true      
    }
  }
}

By setting updateSnapshots to true in our Cypress configuration, any failing screenshot tests will automatically update their respective baseline images. This is handy when we have a lot of intentional visual changes and want to update all the baselines in one go.

It‘s important to keep our baseline screenshots up-to-date with the latest visuals of our application. Outdated baselines can lead to false positive test failures and make it harder to catch real bugs. Get in the habit of updating the baselines whenever you make visual changes to keep your screenshot tests effective and maintainable.

Running Tests Locally

During development, we can run our Cypress tests locally to get fast feedback as we work. The Cypress Test Runner provides an interactive GUI for running and debugging tests.

To open the Test Runner, use the following command:

npx cypress open

This launches the Test Runner window, where you can see a list of all your test files. Clicking on a test file will run that specific test in a new browser window.

The Test Runner makes it easy to interactively develop and debug tests. You can use the built-in inspector to view the state of your application at each test step, manually interact with your app while the test is paused, and quickly re-run tests after making changes.

For headless testing in CI environments or to run the full test suite locally, you can use the cypress run command:

npx cypress run

This runs all your tests in headless mode and outputs the results to the terminal. Any failing tests will be logged with error details and links to the diff images for debugging.

For screenshot tests, you may want to generate an HTML report for easier visual debugging. The cypress-mochawesome-reporter plugin generates detailed test reports with inline screenshots and expandable test steps.

Continuous Integration

Running screenshot tests in CI is crucial for catching visual regressions before they are deployed to production. By integrating Cypress into your CI pipeline, you can automatically run the tests on every push and pull request.

The specifics of setting up Cypress in CI will depend on your CI provider, but the general steps are:

  1. Install Cypress and any required dependencies
  2. Run cypress run to execute the tests in headless mode
  3. Store the generated screenshots and videos as build artifacts
  4. Fail the build if any tests fail

For pull requests, it‘s useful to post the test results as a comment on the PR for quick feedback. The upload-artifact and github-script GitHub Actions make it straightforward to upload the test artifacts and post a PR comment with links to the diff images.

Here‘s an example GitHub Actions workflow that runs Cypress tests and posts the results to the PR:

name: Cypress Tests

on: [pull_request]

jobs:
  cypress-run:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Run Cypress tests
        uses: cypress-io/github-action@v2
        with:
          command: npx cypress run
          browser: chrome

      - name: Upload screenshots
        uses: actions/upload-artifact@v2
        if: failure()
        with:
          name: cypress-screenshots
          path: cypress/screenshots

      - name: Post test results to PR  
        uses: actions/github-script@v3
        with:
          github-token: ${{secrets.GITHUB_TOKEN}}
          script: |
            github.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: ‘## Cypress Test Results\n<details><summary>Screenshots</summary>\n\n![Failed screenshot](https://github.com/my-org/my-repo/blob/screenshot-uploads/cypress/screenshots/my-test-spec.js/failing-test.png?raw=true)\n</details>‘
            })

This workflow uses the official cypress-io/github-action to run the Cypress tests. If any screenshot tests fail, the generated screenshots are uploaded as build artifacts and a comment is posted to the PR with the failure details and a link to the diff image.

By gating deployments on passing Cypress tests, we can ensure that no visual regressions make it through to production. Visual testing in CI gives us confidence that our application always looks and functions as intended.

Best Practices

While screenshot testing is a powerful tool for catching visual bugs, it‘s important to use it thoughtfully and avoid over-testing. Visual tests can be brittle and require frequent updating as your application evolves.

Here are some best practices to keep in mind when incorporating screenshot tests into your project:

  • Focus on high-value, low-change screens and components. Avoid testing every single visual detail of your application.
  • Combine screenshot tests with functional tests for comprehensive coverage. Visual testing catches visual bugs but can‘t verify functionality.
  • Keep screenshot tests manageable by only capturing the relevant portions of the screen. Avoid full page screenshots unless absolutely necessary.
  • Use dynamic data and time-sensitive elements sparingly in screenshot tests. Exclude dynamic regions from screenshot comparisons to avoid flaky tests.
  • Update baseline screenshots whenever you make intentional visual changes. Strive to keep the baseline images in sync with the current visuals.
  • Monitor test performance as you add more screenshot tests. Large screenshots can slow down test execution. Optimize your tests to keep them fast.

By following these best practices, you can effectively use screenshot testing to catch visual bugs without slowing down development or making your tests difficult to maintain.

Conclusion

In this post, we explored how to add screenshot testing to your project using Cypress and the cypress-image-snapshot plugin. We walked through the setup process, wrote our first screenshot tests, and looked at strategies for handling test failures and debugging visual issues.

We also discussed the importance of running screenshot tests in CI and automatically posting the results to pull requests for quick feedback. By integrating visual testing into your development workflow, you can catch visual bugs early and ensure a high-quality user experience.

As with any testing strategy, it‘s important to use screenshot testing thoughtfully and strike a balance between comprehensive coverage and maintainability. Focus on testing the high-value visuals of your application and combine screenshot tests with functional tests for the best results.

Visual testing is a powerful addition to your testing toolkit, giving you an automated way to detect visual regressions and ensure your application always looks its best. By incorporating screenshot tests into your Cypress test suite, you can deploy with confidence knowing that your application looks and functions as intended.

So go forth and start screenshot testing! Your users (and your future self) will thank you.

Further reading:

Similar Posts