💉

【React-Testing-Library】カスタムフックテスト

2022/01/05に公開

パッケージインストール

次のパッケージをインストールする

@testing-library/react-hooks
@testing-library/react

テスト対象のカスタムフック紹介

カスタムフックにはuseCounter.tsを使う。
(単純なカスタムフックなので本記事では非同期などが絡む複雑なテストは行っていません。やり方がわかったら記事にしようと思います。)

import { useState } from "react";

export const useCounter = (initialCount = 0) => {
  const [count, setCount] = useState(initialCount);

  const increment = () => {
    setCount((count) => count + 1);
  }

  const decrement = () => {
    setCount((count) => count - 1);
  }

  const double = () => {
    setCount((count) => count * 2);
  }

  const reset = () => {
    setCount(0);
  }

  return { count, increment, decrement, double, reset };
}

上記のカスタムフックをApp.tsxでimportする。

import { useCounter } from './hooks/useCounter/useCounter';

export const App = () => {
  const { count, increment, decrement, double, reset } = useCounter(3);
  return (
    <>
      <p>{count}</p>
      <button onClick={increment}>increment</button>
      <button onClick={decrement}>decrement</button>
      <button onClick={double}>double</button>
      <button onClick={reset}>reset</button>
    </>
  );
};

yarn startでこんな感じ

テストファイルの作成

カスタムフックが完成したのでテストを書いていく。
useCounter.test.tsを作成して次のように枠組みを書く。

import { cleanup } from "@testing-library/react";
import { renderHook, act } from '@testing-library/react-hooks'
import { useCounter } from "./useCounter";

afterEach(() => cleanup());
describe("useCounter", () => {
  // useCounterの初期値
  const initialValue = 1;
  it("初期値確認", () => {

  })
})

テストの観点は次の通り
・カスタムフックに渡した初期値が反映されているか?
・カスタムフックから受け取る関数が期待通りの処理を行っているか?

まずはuseCounterの引数がstate(count変数)の初期値として反映されてるか確認する。
カスタムフックはコンポーネント内でしか呼び出せないが、renderHook関数を使うことでコンポーネントの外で呼び出すことができる。

// 省略
describe("useCounter", () => {
  // useCounterの初期値
  const initialValue = 1;
  it("初期値確認", () => {
    const { result } = renderHook(() => useCounter(initialValue));
  })
})

renderHookの戻り値は{result}で受け取ることができる。
上記コードは次のコードのショートハンド

const renderHookResult = renderHook(() => useCounter(initialValue));
const result = renderHookResult.result

result.currentでuseCounterの戻り値にアクセスすることができる。
今回確認したいのは、初期値なのでcountを確認すればよいので次のように書ける。

// 省略
describe("useCounter", () => {
  // useCounterの初期値
  const initialValue = 1;
  it("初期値確認", () => {
    const { result } = renderHook(() => useCounter(initialValue));
    expect(result.current.count).toBe(initialValue);
  })
})

次はuseCounterのincrement関数のテストを書く。
カスタムフックから受け取る関数のテストは'@testing-library/react-hooks'からimportしているact関数で囲む必要がある。実行したあとは、countが+1されているのか確認すればよいので、次のように書ける。

// 省略
  it("increment結果確認", () => {
    const { result } = renderHook(() => useCounter(initialValue));
    act(() => { result.current.increment() });
    expect(result.current.count).toBe(initialValue + 1);
  })

あとは、すべて同じ要領で書いていけばよい。
以下、完成したuseCounter.test.ts。

import { cleanup } from "@testing-library/react";
import { renderHook, act } from '@testing-library/react-hooks'
import { useCounter } from "./useCounter";

afterEach(() => cleanup());

describe("useCounter", () => {
  const initialValue = 1;
  it("初期値確認", () => {
    const { result } = renderHook(() => useCounter(initialValue));
    expect(result.current.count).toBe(initialValue);
  })
  it("increment結果確認", () => {
    const { result } = renderHook(() => useCounter(initialValue));
    act(() => { result.current.increment() });
    expect(result.current.count).toBe(initialValue + 1);
  })

  it("decrement結果確認", () => {
    const { result } = renderHook(() => useCounter(initialValue));
    act(() => { result.current.decrement() });
    expect(result.current.count).toBe(initialValue - 1);
  })

  it("double結果確認", () => {
    const { result } = renderHook(() => useCounter(initialValue));
    act(() => { result.current.double() });
    expect(result.current.count).toBe(initialValue * 2);
  })

  it("reset結果確認", () => {
    const { result } = renderHook(() => useCounter(initialValue));
    act(() => { result.current.reset() });
    expect(result.current.count).toBe(0);
  })
})

Discussion