DEV Community

Husnain Mustafa
Husnain Mustafa

Posted on

Testing in React

In today's world, almost every product, regardless whether it is software or not, is released when the required tests are done over it. Tests assure that the product is up to the mark.

Let's get started with testing your React application. Recently I made a post of developing a basic todo app in React typescript, you can find that here.
For Reference, this is how it looks like:
Image description

We will be writing some tests on this todo app to understand the very basics of React testing.

Installing Libraries

  • Jest, a library for testing purposes, primarily for React.
    npm install --save-dev jest

  • Jest Types, typeScript type definitions for Jest.
    npm install --save-dev @types/jest

  • ts-jest, a TypeScript preprocessor with source map support for Jest that lets you use Jest to test projects written in TypeScript (as our project is in Typescript)
    npm install --save-dev ts-jest

  • React Testing Library, a library which provides us to grab the specific components of the web page (in React) and pass it to testing library such as Jest.
    npm install --save-dev @testing-library/react

Configuration

We need to configure jest.config.mjs You might not find it in your files at first. So you need to run the following command:
npx jest --init
This will ask some questions. You can answer it if you want to customize it according to you but below is a reference picture if you are totally new to it:

Image description

Now you would need to open jest.config.mjs and add the following:

...
...
const config = {
...
     preset: "ts-jest",
...
}
Enter fullscreen mode Exit fullscreen mode

Extension

It would be really helpful if we download this extension in VSCode, Jest Runner.

Image description

This helps run the tests through the GUI rather than writing the path of file by ourselves in the CLI.

Image description

What we are going to test

There could be many things to test, but in this tutorial we will be covering the following:

  1. UI Rendering, i.e whether the component is being rendered or not.
  2. State changing, i.e when I click on 'Done' button, does the card moves from 'Todo' state to 'Done' state.

Testing UI Rendering

  1. Make a file in /src, named as 'App.test.tsx'. This would be the file where we will be writing our tests.

  2. Testing:

import { render, screen } from "@testing-library/react";
import { TodoPage } from "./pages/todo";
import { DummyTodoList } from "./dummydata";

describe("Todos", () => {
  it("should render all the data", () => {
    render(<TodoPage />);
    const { getByText } = screen;
    DummyTodoList.forEach((item) => {
      expect(getByText(item.title)).toBeTruthy();
    });
  });
});
Enter fullscreen mode Exit fullscreen mode
  1. Explanation:
  • describe (provided by jest) is used to describe a test suite, group of related tests. It takes 2 args, first, a string, which describes what test suite is related to. Second, a function, which will have different tests.
  • it (provided by jest) is used to describe an individual test. It is written inside the function provided as second parameter to describe. it also takes 2 parameters, a string, describing the exact test. Second, a function which would actually contain the logic of testing.
  • render is a function of react testing library, which renders the component.
  • screen is an object provided by the react testing library that provides multiple functions, like getByText, to access the element in the DOM
  • expect (provided by jest) is a function which takes an one argument, which we need to test, like the element we are passing, and returns an object which provides multiple functions like toBeTruthy(), meaning it is found in the DOM.
  1. Result

Image description

Examples of expect for understanding:
expect(3).toBe(3) - test pass
expect(<any function>).toBeCalled() - test whether the given function is called
expect(<any function>).toBeCalledTimes(2) - test whether the given function is called 2 times
expect(3).not.toBeGreaterThan(1) - test fails as 3 is greater than 1, note that there is **not**
expect(false).toBeTruthy() - test fails as false is not true

Testing todos in respective tabs

Now we want to test that our dummy data is in the respective tabs, i.e we want to check that the todo which is unchecked must be in the Todo tab and checked todos must be in Done tab.
One way of doing that is, we verify that the text is crossed, i.e style.textDecoration is 'none' in unchecked and style.textDecoration is 'line-through' in checked.
We can do it by (inside the function provided as second parameter of describe):

it("should render all the data in the respective tabs", () => {
    render(<TodoPage />);
    const { getByText } = screen;
    const uncheckedItems = DummyTodoList.filter((item) => !item.checked);
    const checkedItems = DummyTodoList.filter((item) => item.checked);
    uncheckedItems.forEach((item) => {
      expect(getByText(item.title).style.textDecoration).toBe("none");
    });
    checkedItems.forEach((item) => {
      expect(getByText(item.title).style.textDecoration).toBe("line-through");
    });
  });
Enter fullscreen mode Exit fullscreen mode

Result

Image description

Testing whether clicking on checkbox makes our todo checked/unchecked

it("should move unchecked todo to Done tab when click on checkbox", () => {
    render(<TodoPage />);
    const { getByText, getAllByRole } = screen;
    expect(getByText(DummyTodoList[0].title).style.textDecoration).toBe("none");

    const firstCheckBox = getAllByRole("checkbox")[0];
    fireEvent.click(firstCheckBox);
    expect(getByText(DummyTodoList[0].title).style.textDecoration).toBe(
      "line-through"
    );
  });
Enter fullscreen mode Exit fullscreen mode

Note that we have used, getAllByRole, also provided by screen, fetches all the element having a specified role. Checkbox has role of checkbox. You can read more about the different roles of different elements here

Then with all the elements, we selected the very first element by using [0]. This gives us the first checkbox in the DOM. We confirmed that the todo is not checked and is under Todo Tab by simply checking the styles.textDecoration as 'none'.

Then we clicked on the checkbox we first fetched, by using fireEvent (also provided by React Testing Library). And then we verified that todo is checked by, again, checking style.textDecoration as 'line-through'

Result:

Image description

Github Repo

Tests are pushed to this repo


Hope this article helped you understand the very basics of testing. Keep learning ❤️

Top comments (0)