Testing in React
Tags: webdev, testing, react, tutorial
Published: 2023-09-21 19:26:25

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](https://dev.to/husnain/lets-make-your-first-react-typescript-todo-web-app-3e8c). 
For Reference, this is how it looks like: 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ruipw0jndo2sbggul647.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b9mqcfopnomo4en6j1j4.png)

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

```
...
...
const config = {
...
     preset: "ts-jest",
...
}
```

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bqm80f954y2hortdqs6w.png)


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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lv3up5qua33wid9wwwhn.png)



## 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();
    });
  });
});
```
3. 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.

4. Result

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f0wdozjzzbrri9urf7bz.png)



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");
    });
  });
```

Result

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5wcw7dw76wy42mfralec.png)

### 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"
    );
  });
```
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](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0s06n7uzzv5pgc0y2ekt.png)


## Github Repo
Tests are pushed to this [repo](https://github.com/kito-arch/React-Typescript-Todo-App)

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