⛏️

React Testing LibraryでReact Queryをテストする時の注意点

2022/10/14に公開

v4からTanStack Queryと名前を改めたReact Queryさんのテストを書く時のお話です。

テスト環境はavaReact Testing Libraryを使います。

https://tanstack.com/query/v4/docs/guides/testing

チュートリアルに倣って、

import test from 'ava';
import { ReactNode } from 'react';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
import { renderHook, waitFor } from '@testing-library/react';

const queryClient = new QueryClient();
const wrapper: React.FC<{ children: ReactNode }> = (props) => (
    <QueryClientProvider client={queryClient}>{props.children}</QueryClientProvider>
);
const useFoo = () => useQuery(['customHook'], () => 'foo');

test('query ok', async (t) => {
    const { result } = renderHook(() => useFoo(), { wrapper });
    await waitFor(() => t.true(result.current.isSuccess))
    t.is(result.current.data, 'foo');
});

みたいなのを書いていくと、どうにもテストが通りません。

waitForで待つ時にisSuccessのフラグが立つ前に判定してしまうとテストが落ちます。それはそうだね。

このwaitFor中にassertionを書くようになったのはReact18になってからの形のようです。だからと言って、ここでawait waitFor(() => result.current.isSuccess);とかすると、

result.current.dataが取れません。

waitFortrueが返ってくるまで待ってくれる、みたいな挙動を想像するのですが、現実はそうはなりません。試しに

await waitFor(() => {
    console.log(JSON.stringify(result.current));
    return result.current.isSuccess;
});

とかで回してみると一度しか出力されず、当然のようにisSuccessfalseでした。

どうすんだこれと思ってwaitForのドキュメントを見ると、

https://testing-library.com/docs/dom-testing-library/api-async/#waitfor

サンプルコードに

// Wait until the callback does not throw an error. In this case, that means
// it'll wait until the mock function has been called once.
await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1))

こう書かれています。なるほど、待つ条件が成立しない時は例外を投げろと? Jestとかだとassertionが通らない時に例外を吐いてくれたりするんですかね。

test('query ok', async (t) => {
    const { result } = renderHook(() => useFoo(), { wrapper });
    await waitFor(() => {
        if (!result.current.isSuccess) {
            throw Error('wait');
        }
    });
    t.is(result.current.data, 'foo');
});

これで待ってやると、無事にテストが通りました。めでたしめでたし。

Discussion