The ultimate guide to testing your frontend with modern tools

A guide to automated testing for React with React Testing Library, Cypress & Cucumber

David Minkovski
6 min readDec 8, 2021

--

Motivation

The frontend is the client-side part of your application.
Everything that is visible to your users is part of the frontend.
To put it in other words — frontend testing is about graphical user interface (GUI) testing.
So you get a better picture, if you are developing an online shopping platform, frontend testers check whether the look and feel of the website are aligned with the requirements. Also, they make sure things work properly and feel the way the user experience designer envisioned it.
So why is this so important?
Well…because our end users judge the book by its cover and have no clue about the backend side of things, they only notice when there’s some issue they can see. Hence problems in the UI stick out and get noticed immediatly. A company that wants to be successful thus needs to provide a great and consistent user experience. This means to make sure that the application runs fast and without error, no matter which device or browser is used.

Welcome to frontend testing!
Frontend testing is about performing tests on multiple devices and browsers to make sure the app is free of errors and will deliver a good experience to the users.

source: https://www.cypress.io/

Automation

Probably you are already familiar with manual testing. For example, you have implemented an input field into a form. Afterwards you open the webpage click into the input field to check whether it works and submit the form. This was a manual test. Performing this test for everything you implement over and over again is tiredsome and not scalable. At a certain point you would have no time to implement anything anymore and would spend all day testing. This is where automatization comes into play. Using automated concepts we can be sure that features will continue to work as they should.
Automated tests also help especially with so called edge cases. Use cases we tend to forget, like checking a box for some sign up process.

Good reasons to automate testing

  1. Update your code with confidence!
    Good tests allow you to refactor code with confidence that you are not breaking any functionality and without changing the tests.
  2. Documentation included, for FREE!
    Tests come in handy because they explain how your code works with its expected behavior. Tests, not like written documentation often, have to be always up to date.
  3. Bugs gone forever — almost!
    By writing up test cases for every bug, you can be sure that these will never come back. Further writing tests will improve your understanding of the code and the requirements. Using testing you might find issues that you could miss otherwise.

What are we testing?

source: https://martinfowler.com/articles/practical-test-pyramid.html

UNIT TESTS
These are focused on the smallest entity of your frontend applications.
Usually these are components in the React context, but cover interfaces, classes and methods. These simple and small tests check whether the output of a small part equals the expected. The checks are predefined and completely isolated.

INTEGRATION TESTS
Like the name suggests, these type of tests put multiple code components together and test their interaction. In React these are usually built of views that integrate multiple components.

UI / END-TO-END TESTS
These tests are the holy grail since they are testing the application just like a real user would. They test the entire system as a whole.

Unit & Integration Tests

Before we start — “Good tests are deterministic, they never depend on the environment”.

This means a test should not be flaky — that sometimes passes and sometimes doesn’t. Some possible reasons are:
different timezone, other filesystem, a state (that isn’t cleared before each test), a dependency regarding the order of the tests, timeouts for testing asynchronous behavior.

Technologies we use:

  1. react testing library
  2. React
  3. Jest
//error-boundary.spec.tsimport "@testing-library/jest-dom";import ErrorBoundary from "./error-boundary";
import React from "react";
import { render } from "app/test/test-utils";
beforeEach(() => {
console.error = jest.fn();
});
describe("Error boundary", () => {
it("listens to and handles errors", () => {
const Child = () => {
throw new Error();
};
const { getByText, unmount } = render(
<ErrorBoundary>
<Child />
</ErrorBoundary>
);
const errorMessage = getByText("Error");
expect(errorMessage).toBeDefined();
unmount();
});
});

We can then run our unit or integration tests using the jest runner:

npm run jest --config jest.config.js

Integration & UI / End-To-End Tests

The core of our test suite are integration & ui tests. These test components with each other and as an end user. Thus they make sure everything works smoothly together from end user perspective.
To make the most of it — we can make use of Cypress and Cucumber.

Cypress is a free, open source automation test tool developed for modern web applications based on modern frameworks like React. It provides a UI to indicate which tests and commands are running, passed and failed.

But why cucumber? I am not hungry!

There are several tools for a Behavior Driven Development Framework, but the most commonly used BDD tool is Cucumber. BDD is a variant of Test Driven Development (TDD). Human-readable descriptions of software requirements are used as the basis for tests.
This approach helps us to write the tests in an understandable vocabulary for everyone involved in the project. For this reason, even someone without a developer background can get an idea of ​​the tests carried out.

For Cypress we can only write our tests in Javascript / TypeScript. However, if we want to use Cucumber, we convert the Java Script test to “Gherkin” (the language for Cucumber).
Gherkin consists mainly of the four main keywords: Given”, “When”, “And” and “Then. Using the keywords, we can use the JavaScript code that has already been used.

Cypress Example

describe("Form Test", () => {
it("Can fill out a form", () => {
cy.visit("/");
cy.get("form");
cy.get('input[name="name"]').type("David").should("have.value", "David");cy.get('input[name="email"]')
.type("david@medium.com")
.should("have.value", "david@medium.com");
cy.get("textarea")
.type("This is a test")
.should("have.value", "This is a test");
cy.get("form").submit();
});
});

Given When you use Cypress and Cucumber Then you Rock!

//integration/formular/formular.ts
import {
Before,
Given,
Then,
When,
And,
} from "cypress-cucumber-preprocessor/steps";
Given("we are on the form page", () => {
cy.visit("/");
cy.get("form");
});
When("we have entered the name {String}", (name) => {
cy.get('input[name="name"]').type(name).should("have.value", name);
});
And("we have entered the email {String}", (email) => {
cy.get('input[name="email"]').type(email).should("have.value", email);
});
And("we left the comment{String}", (comment) => {
cy.get("textarea").type(comment).should("have.value", comment);
});
And("we have submit the form", () => {
cy.get("form").submit();
});
Then("we are on the start page", () => {
cy.url().should("include", "start");
});

Feature Test File — Yes, simple & beautiful isn’t it?

//integration/form.featureFunctionality: Testing the form input
Scenario: F-01 Fill out the form and submit it
Given we are on the form page
When we have entered the name "David"
And we have entered the email "david@medium.com"
And we left the comment "This is a test"
And we have submit the form
Then we are on the start page

Now comes a Bonus on top. Not only do you get an amazing interface to work with — this bad boy can do Screenshots and thus Visual Regression Testing as well!

source: https://www.cypress.io/features

Summary

In this article I tried to underline the importance of testing your frontend (GUI). We need tools that support us in making sure that our products provide a great and consistent user experience.
Such tools make sure that the app is free of errors and will deliver a good experience to the users. By introducing you to the React testing library & Cypress with Cucumber — I tried to show you these amazing test frameworks and their advantages:

  • Easy to install and configure
  • Fast by running the tests during development and right in your application
  • Have a powerful real-time debugger
  • Easy to write because TypeScript - YAY!
  • Useful and simple API
  • Super detailed documentation
  • Very active community and regular updates

I hope that this article was able to provide you with a little insight to the topic of UI/UX tests and bring forth some curiosity about the tools.

Curious about more?

My newsletter is a burst of tech inspiration, problem-solving hacks, and entrepreneurial spirit.
Subscribe for your weekly dose of innovation and mind-freeing insights:
https://davidthetechie.substack.com/

--

--