DEV Community

Marcus Stamström
Marcus Stamström

Posted on

Making E2E testing easy with Cypress

Cypress: Making E2E testing easy

Cypress is an awesome E2E testing framework made to be consistent, fast and easy to use. It is built from scratch so no Selenium. I recently tried it and I found Cypress to be super easy to setup and get started. It was easy to write tests and it really feels fast and solid. Cypress also comes with a great test runner site where you can see your test run live as well as debug tests and much much more.

What we will go through in this article:

  • go through what E2E tests are in the perspective of web apps
  • learn about Cypress and its features
  • understand how to setup Cypress
  • learn how to write E2E tests with Cypress
  • go through how to use Cypress test runner

What and why E2E test?

Testing is all about quality. We want to make sure that our web apps don't contain bugs. Unit testing tests as small parts as possible and testing all the edge cases. E2E testing, on the other hand, tests the whole functionality, from GUI to API and back. In E2E tests, we usually stick to the happy paths, since we covered all the edge cases in our unit tests.

My definition of E2E testing for web apps:

E2E testing is to test the web app from a user perspective

We are testing the application automatically in the browser to make sure that all functionality holds together and stays consistent and correct as the application evolves. I usually see one E2E test as testing one flow in a user story, the E2E test should take the same steps as when testing a user story manually and produce the same end result.

When E2E testing, it doesn't really matter which framework we use. Consider the web app as a black box that we test through the GUI.

What we will test in this article

The test examples in this article will be based on one view from my weather application, which looks like this.
Weather app page

Cypress

The web has evolved. Finally, testing has too.

Cypress is an E2E testing framework. It is built from scratch to reduce inconsistent results and improve speed. It works for both unit, integration and E2E tests. It's free, open-source, has a test runner locally installed and a dashboard service for recording test runs.

The focus of Cypress is to make it easy to setup Cypress and easy to write, run and debug tests. I really think that Cypress has succeeded with these parts, it's really easy to get going and it's almost as easy to write unit tests with jest as to write E2E tests with Cypress.

Cypress only supports Chrome of the major browser in the current stage, but they are working on cross-browser support for Firefox, Edge and IE11.

Some Cypress features

Cypress has a lot of awesome features. From an E2E perspective, the main features are:

  • Stubs and spies
  • Time travel to see all the steps taken in each test in the test runner
  • Debugging in familiar tools like Chrome dev tools
  • Real time reloads
  • Control network traffic
  • Automatic awaiting
  • Screenshots and videos of failing tests
  • Consistent results

Cypress comes with mocha and chai as the testing framework, so everything is setup and ready to get started.
One of the features mentioned is automatic awaiting. This is really awesome since we now never have to check if an element is in the DOM or perform setTimeout and hope that the element has shown up. Cypress commands automatically waits for the command to be completed and when it's finished it goes to the next command in the test. If the Cypress command isn't finished after 5 seconds, the test will fail.

For Cypress to use automatic awaiting, all Cypress commands are asynchronous, but they are run synchronously.

  cy.get(...).click()
Enter fullscreen mode Exit fullscreen mode

In this example, both get() and click() are asynchronous commands, but get() must be finished before the click command can be run. So each command is like a promise that has to be completed before running the next command.

Why Cypress tests are more reliable?

One of the features in Cypress is consistent results. Cypress is built from scratch to get more consistent results, but why are E2E tests in Cypress more consistent than for example Selenium based E2E frameworks?
Selenium and similar frameworks operate by running outside of the browser and execute remote commands across the network. Cypress is the opposite and instead operates inside the browser. This is why Cypress can produce more consistent test results and is also much faster in my opinion. This also enables Cypress to provide other nice features like mocking and capturing network traffic.

Setting up Cypress

Cypress has done it very easy for us to get up and running. First, install Cypress:

  npm install --save-dev cypress
Enter fullscreen mode Exit fullscreen mode

This will download everything Cypress needs. After that just open Cypress:

  ./node_modules/.bin/cypress open
Enter fullscreen mode Exit fullscreen mode

First time this command is run, Cypress will setup everything for you to start writing E2E tests. Cypress also includes an example folder with many valuable examples and tips.

But this command isn't really about the setup. What this command really does is opening the test runner. In the test runner, we can see our E2E test runs, debug our tests and other things. The Cypress start commands should be added to the package.json.

  "cypress:open": "cypress open",
  "cypress:run": "cypress run"
Enter fullscreen mode Exit fullscreen mode

The Cypress run command will run the tests in the terminal, which is useful on the CI for example.

Writing our first test

After setting up Cypress we can start writing E2E tests. So let's look at how an E2E test can look. In this test, we want to check that the main temperature changes when selecting one of the items in the forecast list.

code example

Cypress commands don't return the element and instead uses chaining. But since Cypress commands are promise like and promises are thenable, we can wait for the Cypress command to finish and the get the element produced by the Cypress command with then().

Note that cypress commands are Promise like, but not exactly a Promise. So we can use then for triggering a callback when the command is finished, but we cannot use async/await.

Stubs (mocks)

Mocking is a great way to get control over certain aspects of the app. Even if we are testing a black box, it's very useful to for instance get control over the API or the geolocation, so we can focus some E2E tests on other aspects.

In Cypress, we mock by using stubs.

cy.stub(object, method)
cy.stub(object, method, replacerFunction)
Enter fullscreen mode Exit fullscreen mode

So, for example, we can stub fetch for certain tests.

code example

This way we can make sure that our app presents exactly the correct information given a certain response from the API.

Stubs should be located in the onBeforeLoad, which makes the stubs available in the current visit of localhost.

code example

Here is another stub, where we want the geolocation to be the center of Stockholm, Sweden. This way when running this test, we will always fetch the weather for Stockholm no matter where this test is run.

When to mock and when to not?

It's really nice to be able to mock certain things and knowing what to expect in return. Though we shouldn't always mock, it should be a good mix. First, write a few E2E tests without stubs if possible. So we first test all the way from end to end and make sure that all integrations work. Then we can start using stubs to make sure our GUI presents exactly the correct information.

Writing tests with mocks

code example
Here is the full test of when we mock fetch to test that our GUI presents correct information. In this test, we use the contain command, which checks if the fetched element contains the input value. This command works both for a specific element and for a container of elements.

Test runner

Cypress comes with an awesome test runner. This is a really great tool and has many features. It's in the test runner that we see our test runs, debug our tests and see that our tests take the correct path through the application. The test runner runs in a chrome instance and thereby giving access to chrome devtools for debugging.

Test runner

So here is a print of one set of E2E tests. We can run all the tests from here and also open chrome devtools to inspect elements or debug our web app.

Test runner in action

Here is a GIF of me inspecting an E2E test. When hovering on different steps in the E2E test, the test runner visualizes which element the Cypress command affects. It also shows which state the GUI was in at each step in a test. This is what Cypress call time travel, we can travel through each step of an E2E test and see how the GUI looked at each step. This is awesome for debugging and just to see that our GUI behaves as expected. Notice also that when hovering on the click event, the test runner shows the before and after of that click.

Conclusion

That is all for this article. Hope you enjoyed it and that I inspired you to try out Cypress for E2E testing.
In this article, we first looked at what E2E testing is. Then we talked about some of the features in Cypress and went deeper into setup, stubs, how to write E2E tests and the test runner with time travel in the end.

If you want to read more about Cypress, they have great docs at Cypress.io with examples and guides for how to write E2E tests.

If you want to see the whole weather app it is live at GitHub pages. If you are interested in seeing more of my E2E tests, the project is available on my GitHub.

Resources

Cypress docs
Github project

Top comments (5)

Collapse
 
trendschau profile image
Sebastian Schürmanns

Thank you for this introduction! After reading it I finally started to test my application with Cypress (had it on my list for month). Indeed, cypress is super easy and it took me only some hours to get my first test-cases up and running :)

Collapse
 
mstamstrom profile image
Marcus Stamström

Thank you!!
Great that you got started with Cypress, it really makes E2E testing easy 😊

Collapse
 
vladimirbag profile image
vladimirbag

Hi!
Nice article.
However, Cypress has lots of limitations, for instance, you can't upload or download files, no multiple tabs or iframes support. While Cypress is a good tool for components testing, for general end 2 end tests I prefer Puppeteer from Google Chrome. I also use CodeceptJS which has a very simple syntax and brings cypress-like experience to Puppeteer.

Collapse
 
mstamstrom profile image
Marcus Stamström

Hi and thank you for the read!

Cypress is still quite new and hasn’t implemented all features yet. I have tried puppeteer as well, but since it runs on top of chromium, it won’t work in for example Firefox or safari, which cypress is working towards fixing. Also found it easier to make test suits with cypress then with puppeteer. But both are good frameworks for sure!!

Collapse
 
vladimirbag profile image
vladimirbag

Puppeteer already supports firefox, and over the support of safari and firefox in Cypress will butt for years. Still, the Puppeteer is made by Google, and therefore the level of integration with browsers and the team there is much better.