Jest, Mocha and Chai are among the most known tools for React testing (and Javascript in general) of applications. In Ackee we use Jest for unit test and integration testing. However, when it comes to testing more complex functionalities, working with Jest is not always easy. When integrating many application parts in your tests you must often deal with complex setup of a test environment, mocking modules/functions and handling of asynchronous events. That is the reason I decided to try the testing tool Cypress which our colleague Lukáš heard about at the Reactive Meetup in Pardubice and I would like to share my experience in this article.

Introduction

Cypress is a tool for testing web application in the real environment of the browser which makes writing and setup of e2e/integration tests easier. Or to be more specific, those that test a complete part of an application, not only stand-alone functions or components. Among other things, it offers stubbing (mocking) of functions and network requests, asserting by mere finding of an HTML element, built-in handling of the asynchronous nature of web applications or inclusion of test into Continuous Integration.

The tool can also be used for unit testing. It is not its primarily purpose though and the tests will run more slowly. There is a plugin for unit testing of React applications.

Cypress test progress window
Cypress test progress window

One of the things that is very well thought-through in Cypress is the UI testing environment and debugging of the tests. In the UI testing environment lets you see everything that is happening in the application during the test and you can switch to any moment of the performed test. Moreover, you have a lot of debugging information available, so finding an error in the test is easy. There is no need to read between the lines in the console and be frustrated that the test is not working. It is of course possible to run Cypress in the console, where it runs in the Electron browser. How it all works is very nicely described in their documentation.

Demonstration

I demonstrate how Cypress works and how test are written in it on an easy example of a web form for adding recipes, using the public Ackee cookbook API.

Tested form

For the sake of simplicity the form will only include 3 fields  – the recipe name, preparation time and description. After clicking on the button “Add”, a POST request will be sent to the API. If the recipe is successfully added, there will be a message about the success under the form, in the opposite case, there will be a message about the failure.

Form for adding a recipe

Installation

In case you are using NPM or YARN in your project, the installation is really simple:

  1.    The package is installed as a dev dependency: npm i-D cypress.
  2.    Into the section scripts of the package.json file, we add a command for running the package: "cypress:open": "cypress open".

The tool will be run by the added npm script npm run cypess:open, a window with the list of template tests will open. In the project, there is a cypress folder generated with several sub-folders, whose structure and purpose is described in the documentation, where you can also find other ways of installation and a thorough manual. The tests are located in the integrations folder.

 

Window with a list of template tests
Window with a list of template tests

Writing a test

The test case for our form is following:

  1.    We navigate to the page with the form.
  2.    We fill out the fields with values – name, duration of preparation and description.
  3.    We click on the Add button.
  4.    We check whether the request to the API has been sent and we return a simulated response (using stub).
  5.    We verify that the message about success is displayed.

Before the code of the test itself, it is necessary to add the URL address where the web is running to the file cypress.json, which ensures a relative address usage within Cypress. In our case localhost:

 

Everything is ready and we can go ahead and write our test which will look almost the same as the test case thanks to the simplicity of the tool.

First, before each test, we run cy.server() and after that cy.route(). These two commands check the behavior of the network request and allow their “stubbing”. Thanks to the cy.route({...}) we define that POST request to the defined URL reacts by the response {} with the default status code 200. Our route has been added an alias postAddRecipe, so that we can later wait for the execution and finishing of the request using cy.wait('@postAddRecipe') after submitting the form. This is a very useful functionality which allow our tests to be independent on an external API . We could also define all scenarios of responses including the unsuccessful ones (404, 500,…).

The rest of the testing code is simple. First, we go to the requested page, after that we fill out the values into the form, submit it, wait for a response and check the display of the message about success.

Here, I will point out that even though the code of the test seems to be synchronous, it is not. Cypress adds commands to the queue internally and in case of failure, it can run several times with a defined timeout. That is why the result of the command cannot be saved to a variable const button = cy.get(‘button’) and be later used, but we have to use an alias cy.get('button').as('myButton'). You can find more about aliases and how it works under the surface in the section Core Concepts of the documentation.

Running the test

And how is the test progressing?

 

Receipt form test progress
Receipt form test progress

A browser window is opened and the whole test is played there. In the left column, we can see all operations that have taken place and by clicking on them, we can return to the given moment to inspect it. Debugging of the test is therefore very easy. There is also other detailed information added to the console, which is very useful when dealing with API requests.

 

Output in the browser console
Output in the browser console

Trouble with Fetch API

Cypress unfortunately does not support Fetch API so far, which is replacing XHR in many modern web applications (for more info visit known issues). If you use it in your projects, stubbing of network requests will not work for you. There is a temporary solution for this missing functionality, which constitutes of replacing Fetch API by a polyfill. This issue is further discussed by the community in the GitHub project.

To Sum Up

In comparison with other existing solutions, Cypress presents a simple and fast way of automatically testing more complex flow of web applications and therefore saving our work with repetitive manual testing. Thanks to the elegant handling of asynchronous request, you can use it to write code as if it were synchronous. It allows testing different responses from external APIs and therefore thoroughly verify the functionality from end to end.

For unit testing (not only of components, but selectors as well), Cypress is less suitable and it is better to use Jest or similar tools where the running of tests will be faster among other things.

You can read about some tips & tricks we find useful in Ackee in our frontend cookbook ?‍?.

Leave a Reply

Your email address will not be published. Required fields are marked *