6 steps to end-to-end testing with Cypress

Alain Chautard
Angular Training
Published in
4 min readOct 10, 2019

--

Cypress has quickly become a great alternative to Protractor/Selenium for end-to-end testing of web applications.

In this post, I’m going to cover the basics of Cypress as well as how to get started with it. You’ll see how easy it is to write useful tests in very little time.

Source code

If you want to try all of the examples in this post, clone the following code repository: https://github.com/alcfeoh/ng-store-cypress.

That repository tests the following web app: http://store.angulartraining.com

Easy to install

You’re one command away from having Cypress in your project:

npm install cypress

Yes, that’s it! The install does take some time though as you’re going to get a full-fledged integrated testing environment.

Running your first test

There are two commands you should be aware of. The first one is:

npx cypress open

This command opens a window where you can decide which tests to run. Once you select one test file or folder, Google Chrome opens with Cypress tooling inside, which looks like this:

The Cypress UI lists all of the tests that ran on the left-hand side

If you want to run your tests for continuous integration, which means without any browser window, then you can use:

npx cypress run

Note that the above command is going to run the Electron window-less browser. It’s not going to use Google Chrome in that case. it’s also worth mentioning that at this point, these are the only two browsers supported by Cypress.

Multi-browser support will happen at some point though.

Using Cypress selectors

Cypress allows you to check things on a webpage by first selecting them. Once you have selected an HTML element, you can run assertions on it.

Here’s an example from my test project (demos/D4-testing-navigation.spec.js):

it('can navigate to the cart page', () => {
// Select an element that displays "My cart" and click on it
cy.contains('My cart').click();
// Make sure that "My Cart" is still visible
cy.contains('My Cart').should('be.visible');
// Make sure that "Your current cart contents" is visible
cy.contains('Your current cart contents').should('be.visible');
});

The above test uses the cy object to select some HTML elements by their text contents. Then we can interact with those elements using click() for instance.

Another option is to select an element using a CSS selector. In that case, we use cy.get() as follows:

it('supports multiple currencies', () => {
cy.get('app-currency-switcher').click();
cy.contains('EUR').click();

The above code selects any HTML element that has a tag name of app-currency-switcher, which is an Angular component in my store.

As a result, those two lines of code click on the following dropdown and then click on the Euro option:

Note that Cypress is very smart with selectors. It is going to wait and retry for up to 4 seconds to find an element on the page. No need to ask Cypress to wait: It’s doing it automatically.

Assertions with Chai and Mocha

Cypress uses Chai and Mocha as Javascript testing libraries. As a result, you can use complex assertions to check every single thing you need on your web page.

Here’s a test that checks the CSS font-weight, size, as well as font-family of our main page’s title — note that I’m even using a regular expression there:

it('displays the right main title', () => {
cy.contains('Welcome to our store')
.should('be.visible')
.should('have.css', 'font-weight', '300')
.should('have.css', 'font-size', '72px')
.and('have.css', 'font-family').and('match', /Segoe UI/);
});

Keyboard events

Cypress makes it super easy to interact with your webpage. Here’s how I can select a text input and type something into it:

cy.get('input').type('Hello, World');

That same type() function can be used to hit specific keys of your keyboard, here it would hit enter:

cy.get('form').type('{enter}');

And many more keys are supported.

Mocking HTTP requests

Last but not least, Cypress can mock any HTTP request to your backend on demand. In this example, I’m returning hardcoded exchange rates so I can test my store without relying on actual exchange rates that vary all the time:

beforeEach(() => {
cy.server();
cy.route('/demos/angular/rates.php', {EUR: 1.5, GBP: 2});
}

The above code runs before every single test, starts a Cypress server that is going to intercept all requests to ‘/demos/angular/rates.php ‘, and then always returns the following JSON data as a response: {EUR: 1.5, GBP: 2}

That way my tests can now check data using those exchange rates:

it('displays 8 license plates', () => {
cy.get('app-license-plate').should('have.length', 8);
cy.get('app-license-plate:nth-of-type(1)').contains('$8');
cy.get('app-license-plate:nth-of-type(2)').contains('$11')
});
it('supports multiple currencies', () => {
cy.get('app-currency-switcher').click();
cy.contains('EUR').click();
cy.get('app-license-plate:nth-of-type(1)').contains('€12');
cy.get('app-license-plate:nth-of-type(2)').contains('€16.5');
});

These are two expressive tests that check that my first product costs $8 in USD and then displays €12 after changing the currency to EUR. Easy to read, easy to maintain!

If you’d like to learn more about Cypress, please let me know in the comments section and I’ll post more tutorials about it.

My name is Alain Chautard. I am a Google Developer Expert in Angular, as well as a founding consultant and trainer at Angular Training where I help development teams learn and become proficient with Angular.

Need help with Angular or Cypress? Let’s schedule some time to talk. I provide coaching and on-site training.

If you enjoyed this post, please clap for it or share it. Your help is always appreciated.

--

--