author-image

David Hunt

End to End testing with Cypress

Testing is an important part of creating any application. It gives you the confidence that your application works the way it is meant to. At Fathom, we use many tools to test our software, to ensure that we create the highest quality applications and minimise issues. Today, I will be talking about web application end to end testing using Cypress.

What is end to end testing?

End to end testing is used to test an application flow from start to finish. Tests are designed to use the application the same way that a user would. This gives you the confidence that changes made to your application don't break something elsewhere, and prevent a user, or potential new customer, from being able to use the application properly.

Why Cypress?

There are plenty of end to end testing tools for web applications, such as TestCafe, Puppeteer and Selenium. Each have their pros and cons.

So why use Cypress?

Cypress is easy to get set up and running

As we will see later, you can get Cypress set up within a matter of minutes, without the headaches.

Large community

Cypress has a large community of users who are always helpful in sharing their experiences. This is helpful in cases where you come across an issue. If you are having a problem, someone else has probably dealt with it before.

Live preview

Cypress tests can be run in a modified version of Google Chrome, that comes with the install, that will allow you to preview the tests as they happen. Cypress also by default creates a video and screenshots to help you try debug issues.

Simple Syntax

The syntax used for Cypress tests are very simple and easy to read/write. After a little practice you will have the knowledge to create useful UI tests.

Tutorial

Setup

For this tutorial we are going to test the Cypress Kitchen Sink site. However you can set up Cypress to test any website.

Install Cypress to your project. For this tutorial we created a basic nodejs application using npm init. However you can install Cypress to the project for your web application.

npm install Cypress --save-dev

Once installed you can run

npx Cypress open

to open the Cypress Test Runner.

Image of Cypress test runner

You will see a list of example tests in the Cypress Test Runner window. These are a great way of seeing all the ways you can interact with your web application. For this tutorial however, we are going to make our own test.

Guide to the UI

Run any of the example tests by double clicking on one of them in the Test Runner. A modified version of Chrome will pop up. This is where we can see our tests being run.

Image of Cypress UI

On the left we have our tests. Our tests are contained in test suites, and our tests are made up of commands.

On the right you will see a preview of what the tests are performing.

You can click through the commands and see snapshots of the web application at that point in the test. You can even open up the console to see what was logged at that point in time. This is obviously very helpful for debugging issues, as we will see later.

Creating tests

Open up your project in your preferred IDE. You can find the example tests in cypress/integration/examples. All of your tests will be created in the cypress/integration/ directory.

Create a new file here. We are going to call it example.spec.js.

Add the following into your file

describe('Simple tests', () => {
  it('Visit Fathom website', () => {
    cy.visit('https://fathomtech.io/');
  });
});

Open up the Cypress Test Runner and run the example.spec.js file. The modified version of Chrome should open up and navigate to the Fathom website. Very simple.

But let's break the test down line by line.

describe('Simple tests', () => {
  ...
});

The outer block defines the test suite. This is a collection of tests that logically work together. Say if you want a set of tests to validate the sign up screens, you can create a test suite just for that.

  it('Visit Fathom website', () => {
    ...
  });

The next block defines a test. In this block you could write tests to examine the inputting of an email/password into the sign in page.

  cy.visit('https://fathomtech.io/');

Inside the test block we have commands. This is where we can say what we want to do or check during a test. In this example we simply have it set to visit the Fathom website.

Ok now let's actually test something. Replace the previous code block with the following.

describe('Simple tests', () => {
  it('Using query commands', () => {
    cy.visit('https://example.cypress.io/commands/querying');
    cy.get('button').should('contain', 'Button')
  });
});

This test looks for a button, and this button should contain the word ‘Button’ on it. How cool!

However there is one issue with this test. This test looks for the first button it can find. Thankfully on this page there is only one button, but in more advanced web applications, there could be multiple. Update the test to get the button by its CSS ID, #query-btn. It is recommend however to use data-cy attributes to select elements. You can find out more information on best practices here

So hopefully you've got to this point without any issues. So let's create one! Update your previous test to check that the button should contain Fathom. Go back to the Cypress Test Runner. You will see the runner attempt to find Fathom in the button, before timing out and failing the test.

Image of failing test

You can see on the left of the testing window a red box around the test that failed and an explanation of why it failed. You are able to click through the commands on the left and view a snapshot of the website at that stage in the test. Click the Get command for the button, you will see on the right that the button is highlighted. The next command tells us that it expected the button to contain Fathom. This is why it failed. You are able to click through all the commands for all the tests, not just failed ones.

CypressError: Timed out retrying: expected '[ <button.navbar-toggle.collapsed>, 3 more... ]' to contain 'Fathom'

Change the test back so that it passes again.

Now let's interact with the web application. One of the most important interactions that can occur on a website is typing into an input. Add the following test to your test suite

  it('Should type into fields', () => {
    cy.visit('https://example.cypress.io/commands/actions');
    cy.get('.action-email')
      .type('david@fathomtech.io').should('have.value', 'david@fathomtech.io')
  });

Run the test to see what happens. We can see that it gets the input field by its CSS class and then types in the email. It then checks the input to see that it contains the correct value.

Now create a new test. In this test we want to perform another important action…clicking! In this test add

  cy.get('.action-btn').click()

This will find the button with the CSS class action-btn and click it. WOW!

So as you can see, the syntax in Cypress is very simple, you can almost guess it.

But what if we are using a form and need to submit information?

  it('Submit form', () => {
    cy.get('.action-form')
    .find('[type="text"]').type('ILOVECypress')
    cy.get('.action-form').submit()
      .next().should('contain', 'Your form has been submitted!')
  });

This test is a little bit more complicated than before. First we get the form, then find in the form a field of type text and type in our coupon code. Next, we click submit. The next() command is used to get the next child element within the form, which should be text.

Look at that, you are now able to write most of what you would need for your tests in only a few simple examples! You can learn a lot about the various commands in the Kitchen Sink.

Using Cypress in CI/CD

For cases where you need to test your web application before deploying the changes, you will need to run it on a local server. However this can cause issues in CI/CD as the server running never exits, therefore preventing the tests from being run.

At Fathom, to solve this we use another npm package called start-server-and-test.

You can then set up your npm test script to something like this:

"test": "start-server-and-test start http://localhost:3030 cy:run"

Linting with Cypress

For those who are using ESLint, take a look at eslint-plugin-cypress to prevent linting errors from popping up.

Downsides to Cypress

Not everything is so hunky-dory with Cypress however. As with any tool, there are use cases where it may not be ideal, and worth looking at the alternatives.

Limited browser support

Currently Cypress support is limited to the Chrome bowsers and Electron. This can be an issue if your project requires testing across multiple browsers. Cypress does allow you to manually add in support for other Chromium based browsers, such as Brave or the latest Microsoft Edge. However in some use cases you may require to test older, more problematic browsers, such as the notorious Internet Explorer!

Inability to persist local storage

After each test is run in Cypress, local storage and cookies are cleared, this is to prevent state building up. Currently there is no way to persist local storage in Cypress. This can be an issue where your authentication provider uses local storage for storing authorisation data. The widely suggested solution is to use a beforeEach to re-authenticate the user before every test, however this is not ideal.

Parallelization support

In order to run your Cypress tests in parallel, you require multiple machines, each one to run a single instance of a test. This is not ideal in situations where you have only a single machine to run your tests. Alternatives such as TestCafe do a far better job, allowing you to simply run multiple instances of a browser.

Conclusion

Cypress is a great tool for those who want to create useful end to end tests with very little effort. It also makes it very easy to debug issues with its live preview, snapshots, videos and screenshots. It does fall down in certain areas, and may not be the best solution for certain projects. You can learn more about Cypress here.

SO WHAT DO YOU THINK ?

We love to talk about ideas, innovation, technology and just about anything else. Really, we love to talk.

Contact fathom