⛏️
React Testing LibraryでReact Queryをテストする時の注意点
v4からTanStack Queryと名前を改めたReact Queryさんのテストを書く時のお話です。
テスト環境はavaとReact Testing Libraryを使います。
チュートリアルに倣って、
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
が取れません。
waitFor
はtrue
が返ってくるまで待ってくれる、みたいな挙動を想像するのですが、現実はそうはなりません。試しに
await waitFor(() => {
console.log(JSON.stringify(result.current));
return result.current.isSuccess;
});
とかで回してみると一度しか出力されず、当然のようにisSuccess
はfalse
でした。
どうすんだこれと思って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