A Brief Guide to Test React Components with Hooks
Tags: javascript, react, testing, hooks
Published: 2019-06-06 23:05:54

_Read the original article [here](http://rahmanfadhil.com/testing-react-hooks)_

Testing is a fundamental skill for every web developer to build high quality and stable web applications. But, it's also intimidating especially for those who just getting started with web development. But it turns out: with modern frameworks and libraries, testing is not that hard.

A lot of developers avoid to test their applications, and you shouldn’t!

## Getting started

To get started with this tutorial, you need to have a good understanding about the basics of [React](https://reactjs.org) and [React Hooks](https://reactjs.org/docs/hooks-intro.html) (e.g. `useState` and `useEffect`).

I also have prepared the code of this project. You can check it out right [here](https://codesandbox.io).

## Jest basics

[Jest](https://jestjs.io) is a testing framework for JavaScript which focuses on simplicity. It works with other JavaScript frameworks like [Angular](https://angular.io) and [Vue](https://vuejs.org), and most importantly, it works with React using [`create-react-app`](https://facebook.github.io/create-react-app) out of the box!

To find out what is the purpose of jest, take a look at this function below.

```js
function sum(a, b) {
  return a + b
}
```

In this example, we have a `sum` function, which just return a sum of two parameters. To test our function, define a test suite with `test` method provided by jest. Then, we expect our function to return a sum of 2 + 2.

```js
test("function should add numbers correctly", () => {
  expect(sum(2, 2)).toBe(4)
})
```

When you're writing tests, you often need to check some values meet certain conditions. In Jest, you use "matchers" which you can access them trough `expect` function to validate different things.

Here are some basic matchers which you'll frequently use in testing.

- [`toBe`](#toBe)
- [`toBeTruthy`](#toBeTruthy)
- [`toBeFalsy`](#toBeFalsy)
- [`not`](#not)
- [`toHaveLength`](#toHaveLength)

### toBe

Use `toBe` to compare primitive values. It calls `Object.is` to compare values, which is in some cases better for testing than `===` strict equality operator.

For example, this test will validate some properties inside `user` object:

```js
const user = {
  username: "johndoe",
  age: 24,
}

test("is 24 years old", () => {
  expect(can.age).toBe(24)
})

test("has a username", () => {
  expect(can.name).toBe("johndoe")
})
```

### toBeTruthy

Use `toBeTruthy` when you just to check whether the value is true, no matter what the value is.

```js
const user = {
  username: "johndoe",
  age: 24,
  job: null,
  isOld: false,
}

test("property is truthy", () => {
  expect(can.username).toBeTruthy()
  expect(can.age).toBeTruthy()

  // This will throw error
  expect(can.job).toBeTruthy()
  expect(can.isOldEnough).toBeTruthy()
})
```

### toBeFalsy

Just the opposite of `toBeTruthy`.

```js
const user = {
  username: "johndoe",
  age: 24,
  job: null,
  isOld: false,
}

test("the property is falsy", () => {
  expect(can.job).toBeFalsy()
  expect(can.isOldEnough).toBeFalsy()

  // This will throw error
  expect(can.username).toBeFalsy()
  expect(can.age).toBeFalsy()
})
```

### not

Use `not` to expect the opposite of the given matchers.

```js
const user = {
  username: "johndoe",
  age: 24,
  job: null,
  isOld: false,
}

test("property is truthy", () => {
  expect(can.username).toBeTruthy()
  expect(can.age).toBeTruthy()

  expect(can.job).not.toBeTruthy()
  expect(can.isOldEnough).not.toBeTruthy()
})
```

### toHaveLength

Use `toHaveLength` to check an object that has a `length` property (e.g. Array, Object, and String) and it's set to a certain number.

```js
const numbers = [1, 2, 3]
const greeting = "Hello world!"
const user = { firstName: "John", lastName: "Doe" }

expect(numbers).toHaveLength(3)
expect(greeting).toHaveLength(12)
expect(user).toHaveLength(2)
```

## React Testing Library

Another tool we use in this project is [React Testing Library](https://testing-library.com/docs/react-testing-library/intro). It's a simple and complete testing library for React that encourage us to write tests with good testing practices. If you've already familiar with React testing before, you should've heard about [Enzyme](https://airbnb.io/enzyme). And it's very similar to what we're using right now.

There are several methods provided by React Testing Library which I found very useful and we're going to cover in this tutorial.

- [`render`](#render)
- [`getByTestId`](#getByTestId)
- [`fireEvent`](#fireEvent)
- [`cleanup`](#cleanup)

Here is some quick guide using React Testing Library alongside with Jest.

### render

Render component into a container which is appended to `document.body`.

```js
import { render } from "react-testing-library"

const { container } = render(<div />)
```

### getByTestId

Find an element/component to test inside a container.

`MyButton` component:

```jsx
<button data-testid="my-button">Click Me!</button>
```

Our test will be:

```js
const { getByTestId } = render(<MyButton />)

const myButton = getByTestId("my-button")
expect(myButton.text).toBe("Click Me!")
```

### fireEvent

A method to fire DOM event.

```js
// Fire a click event
fireEvent.click(getByTestId("my-button"))

// Fire a keyboard event
fireEvent.keyDown(getByTestId("my-input"), { key: "Enter", code: 13 })
```

### cleanup

Unmounts React trees that were mounted with [render](#render).

```js
test("renders into document", () => {
  render(<MyComponent />)
  // ...
  cleanup()
})
```

Run `cleanup` automatically after each test.

```js
afterEach(cleanup)

test("renders into document", () => {
  render(<MyComponent />)
  // ...
})
```

## Testing components

Here is an example of a component that uses hooks.

```jsx
// App.js

import React, { useState } from "react"

function App() {
  const [counter, setCounter] = useState(0)

  return (
    <div>
      <h1>Counter: {counter}</h1>
      <button onClick={() => setCounter(counter + 1)}>Increase</button>
    </div>
  )
}
```

Nothing special here, just a counter component with an `h1` tag and a `button` to increase the counter. So, how to test it?

We're going to expect our "Increase" button will change the value inside the `h1` tag whenever we click it. So, we need to grab the `button` and the `h1` element by using the `getByTestId` method provided by React Testing Library. To make let it happen, we need to add `data-testid` property inside both components.

```jsx
<div>
  <h1 data-testid="counter">Counter: {counter}</h1>
  <button data-testid="increase-btn" onClick={() => setCounter(counter + 1)}>
    Increase
  </button>
</div>
```

Time to write some tests. First, we need to import our component and render it. Also, don't forget _cleanup_ after each test.

```jsx
import { render, cleanup, fireEvent } from "react-testing-library"
import App from "./App.js"

afterEach(cleanup)

test("should increase counter when the button clicked", () => {
  const component = render(<App />)
})
```

Second, grab our elements inside our component using `getByTestId` method.

```jsx
import { render, cleanup, fireEvent } from "react-testing-library"
import App from "./App.js"

afterEach(cleanup)

test("should increase counter when the button clicked", () => {
  const { getByTestId } = render(<App />)

  const counter = getByTestId("counter")
  const increaseBtn = getByTestId("increase-btn")
})
```

Finally, we expect our `counter` element text is "Counter: 0" by default (coz our initial state is 0). Then, fire a click event to the `increaseBtn` element and expect the `counter` text increased to "Counter: 1".

```jsx
import { render, cleanup, fireEvent } from "react-testing-library"
import App from "./App.js"

afterEach(cleanup)

test("should increase counter when the button clicked", () => {
  const { getByTestId } = render(<App />)

  const counter = getByTestId("counter")
  const increaseBtn = getByTestId("increase-btn")

  expect(counter.textContent).toBe("Counter: 0")
  fireEvent.click(increaseBtn)
  expect(counter.textContent).toBe("Counter: 1")
})
```

With those 3 simple steps, you're now able to test your components that uses React Hooks.