Ovie Okeh Programming enthusiast, lover of all things that go beep.

How to write end-to-end tests with Cypress and Node.js

7 min read 2221

End To End Tests Cypress Node

Editor’s note: This tutorial was last updated on 01 March 2022 to reflect updated versions of Node.js and Cypress.

Writing large scale applications can very quickly become complex, and the problem is only exacerbated as teams grow and more people work on the same codebase. Therefore, testing is an essential aspect of software engineering, and is arguably even more important in frontend development. With so many moving parts, writing unit and functional tests alone might not be sufficient to verify the application’s correctness.

For example, with a unit test, you can’t really verify that a particular user flow doesn’t cause issues. In this case, end-to-end (E2E) testing comes in handy, allowing you to replicate user behavior on your application and verify that everything is working as it should. If you’re writing production-grade web apps, writing E2E tests is a no-brainer.

In this article, we’ll take a look at how to write useful E2E tests on the frontend using Cypress. While there are other E2E testing tools like Selenium and Nightwatch.js, we’ll focus on Cypress because of its suite of features, which include time traveling through your tests, recording tests for later playback, and more.

To follow along with this tutorial, you’ll need to have recent versions of Node.js and npm installed. You can access the full source code used for this tutorial on GitHub. Let’s get started!

Table of contents

Setting up Cypress

To get started, we’ll create a new project and set up Cypress. Initialize a new project by running the following commands:

$ mkdir cypress-tutorial
$ cd cypress-tutorial
$ npm init -y

Next, install the Cypress package as a development dependency:

$ npm install --save-dev cypress

Open the package.json file in the root of your project and update the scripts property to the following:

"scripts": {
    "test": "npx cypress run",
    "cypress:open": "npx cypress open"
  },

Save and close the file, then in the root folder of your project, create a file called cypress.json, which is the configuration file where you can customize Cypress’ behavior for this specific project. Add the following code to the file and save it:

{ "chromeWebSecurity": false }

We’ll explore the command above in detail later on in the tutorial.



Writing Cypress end-to-end tests

For those of us who follow the Agile methodology, user stories usually follow a format that looks similar to the following:

"When a user takes a specific action, then the user should see this."

To determine how to write an E2E test for that specific story, you’ll simulate taking the action the user is expected to take through the test, then assert that the resulting application state matches your expectations. When testing frontend applications, that process can usually be broken down into the following steps:

  1. Visit a page on your app
  2. Query an element on that page, i.e., buttons, dropdowns, etc.
  3. Interact with the element, i.e., clicking on buttons, dragging divs, or filling inputs, etc.
  4. Confirm that the new state is correct after the interaction

We’ll write three tests to assert that we can perform certain actions on Wikipedia by mimicking a user’s actions. For each test, we’ll write a user story before writing the actual test.

Although for the sake of this tutorial, we’re using Cypress on a public website, it’s not meant to be used on websites you don’t own.

Before we begin writing the tests, you need to create a special folder to hold your Cypress tests. In the root of your project, run the following commands:

$ mkdir cypress && cd cypress
$ mkdir integration && cd integration

All of our tests will be placed inside the cypress/integration folder, which is where Cypress tries to locate the tests by default. You can change the location for your test files using the integrationFolder configuration option.

Test one: A user performs a search from the homepage

When a user visits the homepage, types in the search box, and clicks on the search icon, then the user should see a new page populated with the results from their search term.


More great articles from LogRocket:


The user story for this example is pretty straightforward, simply asserting the correct behavior for a search action on the homepage by a hypothetical user. Let’s write the test for it.

Inside the cypress/integration folder, create a new test file called homepage_search_spec.js and open it in your favorite text editor:

describe('Testing Wikipedia', () => {
  it('I can search for content', () => {
    cy.visit('https://www.wikipedia.org');
    cy.get("input[type='search']").type('Leo Panthera');
    cy.get("button[type='submit']").click();
    cy.contains('Search results');
    cy.contains('Panthera leo leo');
  });
});

Let’s go through the test and see how it matches the user story we defined earlier.

  • Line three: Visit a page on your app
  • Line four: Query an element on that page and interact with it
  • Line five: Query an element on that page and interact with it
  • Line six: Confirm that the new state after the interaction is correct
  • Line seven: Confirm that the new state after the interaction is correct

A majority of your E2E tests will follow the format above. You don’t need to care about how the app perfoms these actions in the background, instead, all you really need to do is specify the actions that should be performed. The straightforward Cypress syntax makes it easy even for non-programmers to read and understand your tests.

Now, let’s go ahead run the test. Return to your your terminal and run the npm test command. Cypress will look inside the cypress/integration folder and run all the tests there:

Cypress Integration Tests Folder

Your terminal isn’t the only way to run your tests. Alternately, you can run them in the browser, where you’ll get a real-time view of the testing process as Cypress executes it according to your specifications.

In your terminal, run the command below:

npm run cypress:open

A window should pop up that looks like the image below:

Open Cypress Integration Test

To run your tests, click on the homepage_search_spec.js entry, and you should see another window pop up:

Cypress Integration Wikipedia Homepage Test

In the top left corner of the window, you can get a quick view of how many passing and failing tests you have in your test suite. The right side of the window displays what a user would see if they interacted with your application according to the way you specified in the test.

With this simple test, we’ve been able to assert that the search functionality on Wikipedia has satisfied our hypothetical user story requirements.

Test two: A user switches languages from the homepage

When a user visits the homepage, clicks on the language switcher, and selects a new language, they should be redirected to the appropriate domain for the selected language.

Inside the cypress/integration folder, create a new file called homepage_switch_language_spec.js, open it in your text editor, and add the following code into the file:

describe('Testing Wikipedia', () => {
  it('A user can switch languages', () => {
    cy.visit('https://wikipedia.org');
    cy.contains('Read Wikipedia in your language');
    cy.get('#js-lang-list-button').click();
    cy.contains('Yorùbá').click();
    cy.url().should(
      'equal',
      'https://yo.wikipedia.org/wiki/Oj%C3%BAew%C3%A9_%C3%80k%E1%BB%8D%CC%81k%E1%BB%8D%CC%81',
    );
    cy.contains('Ẹ kú àbọ̀');
  });
});

Let’s talk about some Cypress-specific syntax. On line three, we’ve instructed Cypress to visit Wikipedia’s homepage. On line four, we assert that we’re on the page we want to be by confirming that the page contains the text Read Wikipedia in your language.

On line five, we query the language switcher button by its ID and simulate a click action on it. You can find out the element’s ID by inspecting it in Chrome Developer Tools. This brings me to an important concept in writing Cypress tests; there are multiple ways to select a DOM element on Cypress, for example, by targeting its ID, class, or even its tag type.

In this particular test, we’re targeting the button’s ID, but in our previous test, we targeted the tag name and attribute. Be sure to check out the official Cypress docs to explore the different ways of selecting a DOM element.

On line six, we encounter another common Cypress command, which you’ll notice shows up a lot in tests. We assert that there’s an element with the text Yorùbá on the DOM, then we simulate a click on it.

This action will cause Wikipedia to redirect you to the appropriate domain for the language you selected. In our case, we selected the Yorùbá language from West Africa, and we can confirm that we were redirected to the correct page by looking at the current page’s URL.

On line seven, we do exactly that. By calling cy.url(), we get the URL of the current page as text, then we assert that it should equal the appropriate domain for the Yorùbá language. On line eight, we have an extra optional check to see if there is any content on the page in the Yorùbá language.

Bonus fact: Ẹ kú àbọ̀ means “Welcome” in the Yorùbá language and is pronounced Eh – Koo – AhBuh.

When you run the test using the command below, it should pass. Instead of running all tests, as demonstrated earlier, the --spec option is used to run a specific Cypress test:

$ npm test -- --spec .\cypress\integration\homepage_switch_language_spec.js

Run Specific Cypress Integration Test

Test three: A user checks the definition of a word on Wiktionary

When a user visits the homepage and clicks on the link to Wiktionary, then the user should be redirected to wiktionary.org. When a user on wiktionary.org searches for a definition by typing in the search bar and hitting enter, the user should be redirected to a page with the definition of that search term.

Let’s review another straightforward user story. For example, let’s say we want to check for the definition of the word “svelte” on Wiktionary. Before searching for the word, we’ll start off on Wikipedia’s homepage and navigate to Wiktionary .

Inside the cypress/integration folder, create a new file named homepage_search_definition_spec.js, open it, and paste in the code below:

describe('Testing Wikipedia', () => {
  it('A user can search for a word', () => {
    cy.visit('https://wikipedia.org');
    cy.get('.other-project-link')
      .eq(2)
      .click();
    cy.url().should('equal', 'https://www.wiktionary.org/');
    cy.get('#searchInput').type('Svelte{enter}');
    cy.contains('Etymology');
    cy.contains('svelte');
  });
});

Let’s walk through this test as we did the one before. On line three, we visit Wikipedia’s homepage as usual. On line four, we target a class name, pick the third element with the class, and simulate a click on the element. Don’t forget that indices start at 0.

Before we move further, you should beware of a little caveat; if your code relies on auto-generated CSS classes, targeting elements by class names might result in inconsistent test results. In those cases, targeting by ID or tag type and attribute would be the way to go.

On line seven, we assert that we’re on the correct domain by checking the URL using cy.url(). Another caveat to keep in mind; if you’re testing your own app, the need to navigate to another domain may be rare. For this reason, this test would fail if we didn’t add "chromeWebSecurity": false in our cypress.json config.

On line eight, we target the search input by its ID. Then, we simulate a change event by typing in “svelte” and hitting the enter button, handled by {enter}. This action takes us to a new page with the result of our query.

On lines nine and ten, we confirm that we’re on the correct page by asserting that the words “etymology” and “svelte” can be found on the page. Run the test using either the command below or the browser to see you newfound skill:

$ npm test -- --spec .\cypress\integration\homepage_search_definition_spec.js

Run Language Test Cypress

Conclusion

In this tutorial, you’ve seen just how easy it is to verify the correctness of your web app by writing E2E tests using Cypress.

Keep in mind that we’ve barely scratched the surface of what Cypress can do and some of the features it provides. For example, you can even see screenshots of your tests and watch recorded videos by looking under cypress/videos. You can define custom commands to avoid code repetition, mock API response data using fixtures, and more.

Well-written end-to-end tests can save you hours of development time, helping you catch bugs and unexpected behaviors before you merge into production.

Get started by going through the Cypress docs and playing around until you get comfortable enough to start writing live tests. I hope you enjoyed this article, and happy coding!

: Full visibility into your web and mobile apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

200’s only Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. .
.
Ovie Okeh Programming enthusiast, lover of all things that go beep.

4 Replies to “How to write end-to-end tests with Cypress and Node.js”

  1. One comment on your test. You are using cy.contains() and not cy.should()
    cy.contains is a selector and not a validator, sure your test will fail if it cannot find the element but its not an assertion.

  2. Hi, Can you provide some example for reading the date from a json, csv file and use in the test

Leave a Reply