リモート開発メインのソフトウェア開発企業のエンジニアブログです

Test React Custom Hooks with Jest and Testing Library

Prerequisite Installation

Assume that we already installed Jest, Testing Library and SWR.

Custom Hook

If you don’t know what is the custom hooks we recommend you to read the official document about custom hooks.

Before we write the test we should have a custom hook first.

We create a custom hook useIndex to fetch the data from API as follow:

import useSWR from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

export const useIndex = () => {
  const { data, error } = useSWR(
    'https://api.github.com/repos/vercel/swr',
    fetcher
  );

  return {
    data,
    error,
  };
};

Then we use useIndex as follow:

import React from 'react';

import { useIndex } from './hook';

export default function Index() {
  const { data, error } = useIndex();

  if (error) return 'An error has occurred.';

  if (!data) return 'Loading...';

  return (
    <>
      <h1>{data.name}</h1>
      <strong>{data.subscribers_count}</strong>{' '}
      <strong>{data.stargazers_count}</strong>{' '}
      <strong>{data.forks_count}</strong>
    </>
  );
}

Test Custom Hook

Beforehand

There are one property and one function we need to know in Jest:

  • jest.mock – used to spied on the behavior of a function.
  • mockImplementation – used as the implementation of the mock.

There is one function we need to know in Testing Library:

  • render – used to render the component virtually.

Case 1

The first case is that we want to check “Loading…” is displayed when undefined data is returned from the API.

import '@testing-library/jest-dom';
import '@testing-library/jest-dom/extend-expect';

import { jest, test } from '@jest/globals';
import { render } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';

import { useIndex } from './hook';
import IndexPage from './index';

jest.mock('./hook', () => ({
  useIndex: jest.fn(() => ({
    data: {},
    error: undefined,
  })),
}));

test('Assert "Loading..." is displayed when undefined data is returned from the API', () => {
  (useIndex as any).mockImplementation(() => ({
    data: undefined,
    error: undefined,
  }));

  const { getByText } = render(
    <BrowserRouter>
      <Page />
    </BrowserRouter>,
  );

  expect(getByText('Loading...')).toBeInTheDocument();
});

Case 2

The second case is that we want to check “An error has occurred.” is displayed when an error is returned from the API.

import '@testing-library/jest-dom';
import '@testing-library/jest-dom/extend-expect';

import { jest, test } from '@jest/globals';
import { render } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';

import { useIndex } from './hook';
import IndexPage from './index';

jest.mock('./hook', () => ({
  useIndex: jest.fn(() => ({
    data: {},
    error: undefined,
  })),
}));

test('Assert "An error has occurred." is displayed when an error is returned from the API', () => {
  (useIndex as any).mockImplementation(() => ({
    data: {},
    loadingError: { response: { status: 500 } },
  }));

  const { getByText } = render(
    <BrowserRouter>
      <Page />
    </BrowserRouter>,
  );

  expect(
    getByText('An error has occurred.'),
  ).toBeInTheDocument();
});

Case 3

And the third case is that we want to check data is displayed when there is no error.

import '@testing-library/jest-dom';
import '@testing-library/jest-dom/extend-expect';

import { jest, test } from '@jest/globals';
import { render } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';

import { useIndex } from './hook';
import IndexPage from './index';

jest.mock('./hook', () => ({
  useIndex: jest.fn(() => ({
    data: {},
    error: undefined,
  })),
}));

test('Assert data is displayed when there is no error', () => {
  (useIndex as any).mockImplementation(() => ({
    data: {
      id: 218115303,
      name: 'swr',
      description: 'React Hooks for Data Fetching',
      full_name: 'vercel/swr',
      owner: {
        id: 14985020,
        login: 'vercel'
      }
    },
    loadingError: undefined,
  }));

  const { getByText } = render(
    <BrowserRouter>
      <Page />
    </BrowserRouter>,
  );

  expect(getByText('swr')).toBeInTheDocument();
});

← 前の投稿

Reduce the Time Taken for the Testing using Jest and CircleCI Parallelism

次の投稿 →

AWS Fargate を使ってスプラトゥーン3の戦績を stat.ink に定期保存できるようにした

コメントを残す