Visual Regression Testing with Cypress and Angular

Post Editor

Visual regression testing is a robust technic that improves our suite of tests, by literarily taking a screenshot of an element, component, or page in a determined state and then use it to compare the latest state of that element.

4 min read
post

Visual Regression Testing with Cypress and Angular

Visual regression testing is a robust technic that improves our suite of tests, by literarily taking a screenshot of an element, component, or page in a determined state and then use it to compare the latest state of that element.

post
post
4 min read
4 min read

Let me start with two questions:

  1. Have you done modifications to a global CSS sheet, and have no idea if a component got affected?
  2. Have you migrated a library to the latest version and you're wondering if the component is rendering differently than before?

Visual regression testing is a robust technic that improves our suite of tests, by literarily taking a screenshot of an element, component, or page in a determined state and then use it to compare the latest state of that element.

How it works?

Once an element is ready to be tested, we are going to take our golden screenshot and this one will be used in the future to compare the previous and new state and find if something has changed.

Who uses this kind of tests?

It's a common practice in large projects but to give a reference to a giant in the industry, Google uses it in its products. When the Angular team announced their new render engine, Ivy, they mentioned that to test the new engine, they upgraded all their apps to the latest version and ran hundreds of thousands of tests to make sure that the engine was not causing side effects or unexpected renders. They had different flavours of tests, including visual regression tests.

NGConf 2019 Keynote

What should I consider?

  • In the end, these image comparison tests, so it's expected that they aren't as fast as a unit test.
  • Every time you update an element (and there is a screenshot test already in place) and you are sure of the change, you will need to update the golden screenshot

When should I use it?

  • If you're creating several tests to make sure that CSS properties are being applied.
  • If you're about to do a pretty aggressive update in the app the could affect, for example updating a CSS framework library.
  • You want to implement and improve your suite of tests.

Are they the same as snapshots in Jest?

The principle is the same, Jest can take a copy of the code of a component and compare it with the latest every-time the test run. In our case, we're taking screenshots.

How can I implement this in my project?

We are going to use Cypress, which is really modern and cutting edge to that let us run End-to-End test in the front-end.

Note: This is not the only way to do it, most of the testing frameworks have an option or plugin to do it. There are also tools like Applitools that help us to automate this process in a simpler way.

In this example we'll use only open source tools.

Code

  1. Create an Angular project, and then install Cypress along with a plugin by running:
    npm i -D cypress cypress-image-snapshot
  2. Create to NPM script in the package json:
    "cypress": "cypress" and "cypress:open": "npm run cypress -- open"
  3. Run npm run cypress:open. The first time Cypress runs, it will create a folder structure to place your E2E by default.
  4. Now lets update the following files:
    plugins/index.js
const {
  addMatchImageSnapshotPlugin,
} = require('cypress-image-snapshot/plugin');

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

This implements the cypress-image-snapshot plugging.

support/commands.js

import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command';

if (Cypress.config('isInteractive')) {
  Cypress.Commands.add('matchImageSnapshot', () => {
    cy.log('Skipping snapshot ?')
  })
} else {
  addMatchImageSnapshotCommand()
}

The validates that screenshots be created only in headless mode, this way we avoid issues with the size of the screen.

5.  My app is showing a fake Charmander in the app component, so I'm going to create a  new file in integration call home.spec.ts and write a test which will take a screenshot of the main route.

describe('Home screenshot', () => {
  it('should take a screenshot of the home page', () => {
    cy.visit('/');
    cy.matchImageSnapshot('home');
  });
});

Now, let's run this command to generate the screenshots:

npm run cypress run

6. To make sure that the test is working, let's update the Charmarder for a real one and run the same command again:

7.  ¡BOOM! Failed.

If you want to update the golden screenshot, just add this flag--env updateSnapshots=true to the previous command and that's it.

Conclusion

The visual regression tests are an extra shield that We can implement in our project and make sure every-time there is a side effect that could affect the visual layer, we can capture it and find out the problem before the app gets into production. Also decreases human error and the manual QA processes.

Discuss with community

Share

About the author

author_image

Google Developer Expert, Co-Founder of Angular Costa Rica, Passionate for NG, React, VueJS, NestJS, Typescript and all web techs!

author_image

About the author

Mariano Alvarez

Google Developer Expert, Co-Founder of Angular Costa Rica, Passionate for NG, React, VueJS, NestJS, Typescript and all web techs!

About the author

author_image

Google Developer Expert, Co-Founder of Angular Costa Rica, Passionate for NG, React, VueJS, NestJS, Typescript and all web techs!

NxAngularCli
NxAngularCli
NxAngularCli

Featured articles

Angularpost
4 March 20218 min read
Angular Universal: real app problems

Angular Universal is an open-source project that extends the functionality of @angular/platform-server. The project makes server-side rendering possible in Angular. This article will discuss the issues and possible solutions we encountered while developing a real application with Angular Universal.

Angularpost
4 March 20218 min read
Angular Universal: real app problems

Angular Universal is an open-source project that extends the functionality of @angular/platform-server. The project makes server-side rendering possible in Angular. This article will discuss the issues and possible solutions we encountered while developing a real application with Angular Universal.

Read more
AngularpostAngular Universal: real app problems

4 March 2021

8 min read

Angular Universal is an open-source project that extends the functionality of @angular/platform-server. The project makes server-side rendering possible in Angular. This article will discuss the issues and possible solutions we encountered while developing a real application with Angular Universal.

Read more
Angularpost
3 March 20215 min read
View State Selector  - Angular design pattern

As a web developer you may have noticed a repetitive boiler plate code of displaying a loader while an asynchronous request is being processed, then switching to the main view or displaying an error. Personally, I noticed these repetitions both in my code and other developers I work with. And even worse than the repetitive code is the fact that there are no indications for missing state views (such as unhandled errors or a missing loader). <div *ngIf="data$ | async as data"> <ng-container *ng

Angularpost
3 March 20215 min read
View State Selector  - Angular design pattern

As a web developer you may have noticed a repetitive boiler plate code of displaying a loader while an asynchronous request is being processed, then switching to the main view or displaying an error. Personally, I noticed these repetitions both in my code and other developers I work with. And even worse than the repetitive code is the fact that there are no indications for missing state views (such as unhandled errors or a missing loader). <div *ngIf="data$ | async as data"> <ng-container *ng

Read more
AngularpostView State Selector  - Angular design pattern

3 March 2021

5 min read

As a web developer you may have noticed a repetitive boiler plate code of displaying a loader while an asynchronous request is being processed, then switching to the main view or displaying an error. Personally, I noticed these repetitions both in my code and other developers I work with. And even worse than the repetitive code is the fact that there are no indications for missing state views (such as unhandled errors or a missing loader). <div *ngIf="data$ | async as data"> <ng-container *ng

Read more
RxJSpost
26 February 20213 min read
RxJS: Why memory leaks occur when using a Subject

It's not uncommon to see the words 'unsubscribe', 'memory leaks', 'subject' in the same phrase when reading upon RxJS-related materials. In this article, we're going to tackle this fact and by the end of it you should gain a better insight as to why memory leaks occur.

RxJSpost
26 February 20213 min read
RxJS: Why memory leaks occur when using a Subject

It's not uncommon to see the words 'unsubscribe', 'memory leaks', 'subject' in the same phrase when reading upon RxJS-related materials. In this article, we're going to tackle this fact and by the end of it you should gain a better insight as to why memory leaks occur.

Read more
RxJSpostRxJS: Why memory leaks occur when using a Subject

26 February 2021

3 min read

It's not uncommon to see the words 'unsubscribe', 'memory leaks', 'subject' in the same phrase when reading upon RxJS-related materials. In this article, we're going to tackle this fact and by the end of it you should gain a better insight as to why memory leaks occur.

Read more