はじめに
Reactにおいてコンポーネントの中で異なるコンポーネントを呼び出している実装のテストを書くのに苦労したのでまとめました。
実装
import React from "react";
import {Child} from "Child";
export function Parent() {
return (
<div>
<Child test='hoge'/>
</div>
);
}
上記のような実装に対して
Parentコンポーネント内でChildコンポーネントにtest='hoge'をPropsにして呼び出していることを確認するテストを書きます。
テスト
import React from 'react';
import { render } from '@testing-library/react';
import { Child } from "Child";
import { Parent } from 'Parent';
jest.mock('Child') // ①
describe('Parent component', () => {
it('Childが正しいPropsで呼び出されていること', () => {
const child = Child as any // ②
render(<Parent />) // ③
expect(child).toHaveBeenCalled() // ④
expect(child.mock.calls[0][0]).toEqual({test:'hoge'}) // ⑤
});
});
順を追って解説します。
①jest.mock('Child')
ここでChildコンポーネントをmockにします。mockにすることで
このファイル内のテストでChildコンポーネントが使用されるときは本物のChildコンポーネントの代わりに
このmockが使用されることになります。
この記述はdescribeの外に記述しないとテストが通りません。
②const child = Child as any
呼び出されていることを確認したいコンポーネントを変数に格納します。
型推論ができないので as anyで コンポーネントの型チェックを無効にしておきます。
ここでChildが代入されていますが①でChildはモックに置き換えられていますので
代入されるのはmockオブジェクトです。
③render()
Parentコンポーネントをレンダリングします。
④expect(child).toHaveBeenCalled()
toHaveBeenCalled()で child関数が呼び出されたことを確認します。
⑤expect(child.mock.calls[0][0]).toEqual({test:'hoge'})
ここが一番複雑です。
②で説明した通りchildはChildコンポーネントをmock化したオブジェクトです
そのオブジェクトのmockプロパティはモックオブジェクトの情報を持つオブジェクトになります。
次にcallsプロパティはモックオブジェクトが呼び出されたときに渡されたすべての引数を格納しているプロパティです。
次に[0][0]は最初に呼び出されたときに渡された最初の引数なので[0]が2回続きます。
最初に呼び出されたときの2個目の引数なら[0][1]となります。
しかしコンポーネントのPropsの場合Propsがオブジェクトとしてまとめて一つの引数になるので
[0][1]と書いても2つ目のpropsにアクセスすることはできません。
[0][0]と書いてオブジェクトの比較を行いましょう。
まとめ
あまり情報がなく苦労しましたがしっかりテスト書けてよかったです。
このように子コンポーネントをmockにして
親コンポーネントでは正しく子コンポーネントを呼び出していることの確認に留めておくことで
テストファイルをコンポーネントごとに分割するのが容易になります。
間違い等ありましたらご指摘お願いします。