End to end testing using Cypress (part 1)

Ignacio Machado
Devgurus
Published in
6 min readSep 20, 2021

--

Introduction — E2E

If you already know your way around Selenium or Cypress and are familiar with terms like End to End testing and Page Objects you should probably skip to part 2 (coming soon)!

At DMI we build quality, cutting edge e-commerce experiences and are proud members of the MACH alliance. Quality is at the center of our culture and our embrace of continuous testing and automation is a big part of that.

Before we dive into the practical side of building an automation framework based on Cypress I will briefly define some terms. When reviewing the age-old testing pyramid we find at the top the so-called “UI Tests”. These are tests that are in charge of verifying expectations after interaction with the UI. For this article, the UI will be a rendered website and so we require “driving” a browser to achieve that. We are also interested in testing our application from a user perspective, not only driving the browser but doing so as a real user would. In other words, clicking, double-clicking, focusing, waiting for elements to be rendered, etc… This type of testing is often referred to as End to End or E2E.

Mike Cohn’s testing pyramid

End-to-end testing is a staple of automated testing, probably because they have the potential to be the most integrated type of testing. If we think of a website like we would a car, and we propose that we can only perform one test before buying it, we would probably choose to do a test drive. By driving it we would be able to increase our confidence about the quality of the car by simply validating our expectations with reality while using it as we normally would, even though we have tested nothing about its individual parts and have little to no knowledge about its inner workings.

This is, of course, not to say that this layer of testing is the only one you need to have a robust automation framework. More isolated and less integrated strategies, even when testing through the UI, are also important and maybe we can expand on those in later articles.

It’s all about “driving” browsers

So, how do we go about “test driving” our application? There are many ways of driving browsers, the most well-known probably being Selenium. Because of its early introduction to the market as well as having a robust community Selenium has enjoyed popularity for decades. But Selenium is not the only game in town. In the past few years, many very interesting and exciting alternatives were introduced, including the focus of this article Cypress, a framework that aims to fulfill many of the same needs Selenium does but goes about doing it fairly differently.

The aim of this article is not to convince readers to use Cypress over Selenium, I think there are plenty of very insightful comparisons between the two all over Medium that can better help you decide if you are not sure which would be a better fit for your project.

Before we dive into the world of browser automation we should be aware that its greatest strength (high integration and realistic context) is also what makes this layer of testing fairly tricky to implement. If you are not careful E2E tests can be the biggest contributor to flakiness and lead us down the road to maintenance hell, spending most of your time reviewing false positives and fixing your tests to adapt to minor changes in the implementation details.

Hopefully, this article can provide some insights on how to avoid these all too common pitfalls.

To learn more about why you might want to choose cypress for your project you should read https://docs.cypress.io/.

Abstraction as a decoupling strategy

Before we start writing code it is important to plan so that we can ensure that our framework will be readable, maintainable, and scalable. Readable code is greatly helped by adopting the KISS principle. For the maintainable and scalable part, we will be diving into the world of PageObjects.

This pattern attempts to remove coupling between the front end and the tests themselves. If we do it right, there should be no implementation details within the test code. This creates a new layer of abstraction that encapsulates all the specifics about implementation details. Essentially creating an API to our UI.

Be aware that, although this article will focus on Page Objects, there are alternatives like App Actions.

https://martinfowler.com/bliki/PageObject.html

https://www.cypress.io/blog/2019/01/03/stop-using-page-objects-and-start-using-app-actions/

AUT — Application under test

For this article, the object under test will be an e-commerce website, making use of React in the front end and a set of REST APIs for the back end.

We will be focusing on the end-to-end checkout experience, from finding a product within the catalog, adding it to the cart, and completing the order.

Product catalog
Cart
Checkout

First steps — Setting up Cypress

Installing Cypress is fairly simple, just install using npm and you are ready to start testing within minutes. If you want more details about installing in different operating systems or without using npm you can refer to the official documentation.

npm install --save-dev cypress

Once installed we may need to tweak a few things about its configuration. Cypress gives us a handy configuration file in JSON format cypress.json located at the root of our project.

{
"baseUrl": "http://localhost:3000",
"watchForFileChanges": false,
"viewportWidth": 1920,
"viewportHeight": 1080,
"defaultCommandTimeout": 5000
}

For a list of all available options, you can refer to the official documentation. We will be discussing some options in more detail when building our tests.

Once we have cypress installed simply use the following command to execute our tests from the terminal

npx cypress run

or use the following command to open the extremely useful Test Runner, that allows us real-time visual feedback on the execution of our tests

npx cypress open

After you run cypress for the first time the following folder structure will be autogenerated

/cypress
/fixtures
- example.json
/integration
/examples
- actions.spec.js
- aliasing.spec.js
- assertions.spec.js
- connectors.spec.js
- cookies.spec.js
- cypress_api.spec.js
- files.spec.js
- local_storage.spec.js
- location.spec.js
- misc.spec.js
- navigation.spec.js
- network_requests.spec.js
- querying.spec.js
- spies_stubs_clocks.spec.js
- traversal.spec.js
- utilities.spec.js
- viewport.spec.js
- waiting.spec.js
- window.spec.js
/plugins
- index.js
/support
- commands.js
- index.js

For now, we only care about the integration folder that will serve as the location for all our tests.

If we can’t wait to write our tests and want to see cypress in action we can execute any of the example tests using the Test Runner or from the terminal

npx cypress run --spec "cypress/integration/my-spec.js"

Now that we’ve installed and made sure Cypress is working correctly we are ready to start writing our first tests.

In the second part of this article we will be discussing general cypress features like fixtures and tasks, also writing and running our first basic tests on our e-commerce site. See you soon!

For part 2 check End to end testing using Cypress (part 2)

--

--