引き続き React Query の話。
ある関数コンポーネントが useQuery によって取得される外部データに依存している場合に、そのuseQueryで取得される外部データをダミーに置き換えて、コンポーネントテストを書く方法。
なかなかまとまった情報が見つからなかったので記録しておく。
テストには Jest & React Testing Library を使用。
テストしたいコンポーネントのイメージとして、外部APIからユーザ情報を取得して表示するだけの部品を考える。
↓こういうやつ。
/** * APIのデータ取得ロジック */ const fetchUserData = async () => { const res = await fetch("https://jsonplaceholder.typicode.com/users"); if (!res.ok) { throw new Error("fetch error"); } return res.json(); }; /** * users API から取得した情報を表示するコンポーネント */ export const DataViewer = () => { const { data, isError, isLoading } = useQuery("users", fetchUserData); if (isLoading) return <div>loading...</div>; if (isError || data == null) return <div>ERR!</div>; const userCount = data.length; const firstUser = `id=${data[0].id}, name=${data[0].name}`; return ( <div> {userCount}人のユーザ(1人目:{firstUser}) </div> ); };
アプリケーションに組み込む際は、上位コンポーネントとして QueryClientProvider をおく必要がある。
export default function App() { const client = new QueryClient(); return ( <div> <QueryClientProvider client={client}> <DataViewer /> </QueryClientProvider> </div> ); }
さて、これのコンポーネントテストを書く際、外部から取得されるユーザ情報をダミーデータに置き換えて、取得されたデータが正しく表示される部分だけをテストしたいとする。
test("ユーザ情報を表示する", () => { //↓こいつにダミーデータを渡したい const dom = render( <DataViewer /> ); expect(dom.baseElement.textContent).toBe("(期待値)"); });
テストコード内でrenderする際は、テスト用の QueryClient を定義し、QueryClientProvider に渡してテスト用のコンポーネントをラップする。
この QueryClient に対し、setQueryData
を使って手動で任意のデータを設定することができる。
その上で、データの再取得が走らないように staleTime
を Infinity などにしておけば良い。
test("ユーザ情報を表示する", () => { //テスト用のclientを定義する const clientStub = new QueryClient({ defaultOptions: { queries: { retry: false, //※refetchが走らないように staleTime: Infinity } } }); //手動でデータを設定 clientStub.setQueryData("users", [ { id: 555, name: "first user" }, { id: 666, name: "another user" } ]); //テストしたいコンポーネントを QueryClientProvider でラップ const dom = render( <QueryClientProvider client={clientStub}> <DataViewer /> </QueryClientProvider> ); expect(dom.baseElement.textContent).toBe( "2人のユーザ(1人目:id=555, name=first user)" ); });
以上。
コンポーネントを単独でテストしやすく保つにはどちらかというとpropsバケツリレーが基本かと思っていたが、これができるならカジュアルに各コンポーネントから useQuery しちゃってもいいかもですね。
コード全体は以下で確認できます。