Help us understand the problem. What is going on with this article?

React カスタムフックをテストする

カスタムフックとは

React Hooksでは、ユーザー独自のカスタムフックを作成することができます。この機能により、簡単にUIコンポーネントからロジックを切り離し、再利用できるようになりました。
しかし、カスタムフックはReactコンポーネント内でしか動作しないため、ユニットテストを行うには若干コツが必要になるようです。

というわけで、カスタムフックをテストしてみましょう。

環境構築

いつもの(Create React APP + TypeScript)。

カスタムフックの作成

環境構築ができたら、カスタムフックを作成しましょう。

src/useCount.ts
import { useState } from 'react'

const useCount = (): [number, () => void, () => void] => {
  const [count, setCount] = useState(0)

  const increment = () => setCount(count + 1)
  const decrement = () => setCount(count - 1)

  return [count, increment, decrement]
}

export default useCount

カスタムフックの名前は、頭にuseを付けなくてはなりません。「これはカスタムフックなんだぞー」と分かりやすいようにするためです。
ここで作成したフックは、数値の処理操作をまとめたものです。例えば以下のように利用します。

src/App.tsx
// カスタムフック利用例
import React from 'react'
import useCount from './useCount'

const App: React.FC = () => {
  const [count, increment, decrement] = useCount()

  return (
    <div className="App">
      <p>カウント:{count}</p>
      <button onClick={() => increment()}>1増やす</button>
      <button onClick={() => decrement()}>1減らす</button>
    </div>
  )
}

export default App;

キャプチャ.JPG

簡単なカウンターアプリですね。ボタンを押せば1増えたり減ったりします。このように、UIから処理を切り出して別々に記述できることが、カスタムフックを使うことの大きな利点でしょう。

カスタムフックのテスト

まずはReact Testing Libraryを追加しましょう。ちなみに公式でもオススメされています

$ yarn add -D @testing-library/react

React Testing Libraryを使うことにより、いわゆるボイラーテンプレートを省略することができるようになります。
あとはテストを書くだけです。出来上がったテストはこちらになります。

src/useCount.test.tsx
import React from 'react'
import { render, fireEvent } from '@testing-library/react'
import useCount from './useCount'

it('useCountのユニットテスト', () => {
  const TestComponent: React.FC = () => {  // テスト用Reactコンポーネント作成
    const [count, increment, decrement] = useCount()

    return (
      <div>
        <p title="count">{count}</p>
        <button title="increment" onClick={() => increment()}></button>
        <button title="decrement" onClick={() => decrement()}></button>
      </div>
    )
  }

  const { getByTitle } = render(<TestComponent />)  // 仮想DOMにレンダリング

  const count = getByTitle("count")  // HTML要素を取得

  expect(count!.textContent).toEqual("0")

  fireEvent.click(getByTitle("increment"))  // クリックイベントを発火
  expect(count!.textContent).toEqual("1")

  fireEvent.click(getByTitle("decrement"))
  fireEvent.click(getByTitle("decrement"))
  expect(count!.textContent).toEqual("-1")
})

冒頭でも書きましたが、カスタムフックはReactコンポーネント内でしか動作しません。また、カスタムフックの値や関数を外部から直接呼び出すこともできません(もしかしたら私が知らないだけ)。そのため、今回はテスト用のコンポーネントを作成し、HTML要素から間接的に値や関数を呼び出すことでテストを行っています。

引数を変えて何度も関数を呼び出したいこともあると思います。そのときは、個別に引数を設定した関数を用意してボタンにバインドし、クリックイベントで順番に呼び出すといった方法が使えると思います。ボタンをメソッド替わりにするということですね。

HTML要素取得イベント発火には柔軟なAPIが用意されているので、詳細は公式を見てください。

まとめ

カスタムフックを使うとロジックが柔軟に組めるようになります。宣言的UIはReact Hooksで完成に至るという記事もありますし、今後のReact開発ではカスタムフックの知識が不可欠になるかと思います。
カスタムフックで複雑な処理を行うためにも、テスト方法はマスターしておきたいところですね。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした