Getting started with Angular Testing: Cypress

What is E2E testing?

When we keep on adding functionalities to our applications, we also need to make sure the functionalities which are already present does not break. End to end testing make sure that the application does not break even when the new functionalities are added.

It tests the application from start to end in the same way how the user use the application.

In this article lets see how we can integrate E2E testing tool called cypress in angular application with basic tests.

What is Cypress?

Cypress is a one of the end to end front-end application testing tool which replaces previous angular owned testing tool called Protractor. It can run in any operating systems. We can run it as desktop application or with headless mode in command line.

Cypress can do automated end-to-end testing and also unit testing against methods on model and service classes. It can test application with whatever the front-end framework is.

We can also write tests in typescript which it more familiar when we develop applications in angular.

Adding Cypress to Angular project

Step #1: Install Cypress

There are different ways to install cypress based on the operating systems and command line we use.

Install Cypress using npm

npm install cypress --save-dev

Install Cypress using yarn

yarn add cypress --dev

At first it may take few minutes because the package downloads the Cypress binary and install in the selected application, later it will be stored in global cache directory which makes future installations faster.

After the installation, cypress creates a directory in the projects root directory with name cypress and a file with name cypress.json .

Step #2: Configure cypress with cypress.json

The JSON file will be acting as a configuration file for the cypress application similar to package.json for angular. Here we can provide the base URL to the application which cypress will be testing.

{
"baseUrl": "http://localhost:4200"
}

Step #3: Run cypress

We can run cypress in different ways, as a separate application parallel to angular application or headless mode where it runs the tests in command line.

Optionally we can map commands in package.json under scripts.

"scripts":{
"cypress:open": "./node_modules/.bin/cypress open",
"cypress:run": "./node_modules/.bin/cypress run",
}

Run command lets cypress run all the tests in the command line, it doesn’t have any interactive applications. This method will help when it comes to integration with the CI/CD.

npm run cypress:run

Open command lets cypress run as a separate application where users can interact and debug the tests. Application will be like what you see in the below image. We can select which test file to run from this window.

npm run cypress:open
Cypress interactive application

Step #4: Configure TypeScript support

Cypress supports JavaScript by default, but as from angular background it will be more generic if we use TypeScript in cypress as well.

To do that we add types key with array containing cypress string in it to the tsconfig.json in compiler options object.

"compilerOptions": {
"types":["cypress"]
}

Folder Structure

After installation, cypress create a directory in project root. It have different folders in it for different purposes.

  1. Downloads — Path to folder where files downloaded during a test are saved.
  2. Fixtures — Contain dummy JSON data that will be used while testing.
  3. Integration — Contain integration test files with extension spec.ts/test.js.
  4. Plugin — Contains the files that helps to extend the functionality.
  5. Support — Can create reusable functions and export to make us of them in integration testing.

Basic testing terms

Cypress tests are written like most of the other tests. describe defines a test set containing multiple test cases, each one is defined by it . There are written in files under folder /cypress/integration.

It is typically a human tester that look, click or type on where ever it needed. For each action and selection there are methods in the global cy object.

Before we start testing, we need to get the elements that we want to test. To get, we can run a get global command with different kind of parameters like class name, id name, attributes which identifies element.

cy.get("selector")

After selection we can do different type of actions that we need on the element.

Write your first Cypress Test

Lets write a simple test that check the text when button is clicked. When the button is clicked, text will be displayed under the button.

With the knowledge we have, lets create a file(click.test.js) under integration folder and start writing our test.

describe("click test", () => {
it("Show text when button is clicked", () => {
cy.visit("");
cy.get("[data-test=testbtn]").click();
cy.get("[data-test=testtxt]").should(
"contain.text",
"Button is clicked"
);
});
});

Before running tests make sure your Angular application is running. Open the cypress window by running the command npm run cypress:open or npx cypress open . It Opens the window containing all the test files. We can select the file we created, test runs in another window.

Wonderful!! We successfully created a basic test. What if we want to run in headless mode under CI/CD? Instead of npm run cypress:open we can run npm run cypress:run command. When command runs in CI/CD there is no way we can check what went wrong in Cypress. So to overcome this situation, cypress record a video of what happening while testing and will be stored in videos directory under cypress(/cypress/videos). It can also take snapshots and store in snapshot directory under cypress(/cypress/snapshots). All these directories can be configured in cypress.json .

Best Practices

One thing we need to understand is that the tests will run by selecting the elements using class, id or attribute names. The tests will fail when we change class or id names while adding new functionalities to the application.

So, to overcome that we can create data-test(or any unique) attribute with unique values assigned to it for every element.

In the above example we can see data-test attribute is assigned to unique values that helps to identify and run tests.

Asynchronity in cypress

Cypress global cy have await feature built in. It queues and runs the commands internally. Each command will retry multiple times in the given timeout period until the action completes or find element.

Each global cy command is provided with a then method, but those are not like promises.

We can see an example what happen if we use some synchronous conditions in cypress.

describe("click test", () => {
it("Show text when button is clicked", () => {
let isClicked = false;
cy.visit("");
cy.get("[data-test=testbtn]").click();
cy.get("[data-test=testtxt]").should(
"contain.text",
"Button is clicked"
).then(()=>
isClicked = true;
);
if(!isClicked){
throw new Error('Something is wrong');
}
});
});

We got error saying Something is wrong. From this we can understand that the test commands do not run asynchronously. The global cy commands are queued for asynchronous and the remaining synchronous commands runs even before the asynchronous part. let and if condition are ran even before the application start, so the test got failed.

We can change the above test by moving the if condition into the then method. You can run synchronous code only in the then methods.

describe("click test", () => {
it("Show text when button is clicked", () => {
let isClicked = false;
cy.visit("");
cy.get("[data-test=testbtn]").click();
cy.get("[data-test=testtxt]").should(
"contain.text",
"Button is clicked"
).then(()=> {
isClicked = true;
if(!isClicked){
throw new Error('Something is wrong');
}
});
});
});

At this point, we have completed all necessary steps to integrate Cypress with an Angular project, some best practices and asynchronous commands.

Resources

We only took some starting steps in learning cypress. The following links are the list of documentation that we can refer.

  1. Retry-ability — This page describes more about retry functionality on both commands and assertions. When retry commands work and when it does not.
  2. Continuous Integration — This page helps to understand how we can run, configure cypress in CI.

Happy Coding….

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

How to Reverse a Linked List in JavaScript

React Navigation v5 Setup Stack, Authentication,Switch In App | Cab Booking App #4

How to use Webp format (.webp)

React Router 6 with useNavigate Hook

Everything in JavaScript is an Object!

An Instagram gallery with Vue.js and CSS Grid

Real-time CRUD guide: Front end (part 1)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Revanth Oleti

Revanth Oleti

More from Medium

Internationalize your Angular applications using ngx-translate

1. Angular Reactive Forms & Validations in Reactive Forms

How to Setup TailwindCSS in Angular?

How to Setup TailwindCSS in Angular?

Pure vs Impure pipe Performance