Open3

React + Jestでちょっと複雑なことしたい時の備忘録

白雀白雀

あれ、このテストどうやるんだっけ。というときの備忘録として残しておく

白雀白雀

特定のモジュールをMockにすげ替えるテスト。通信結果に応じた処理を変更する、環境変数に応じた処理を変更する等に使える。テストするモジュールの実装を知ってないとできないじゃん?ってなるのが問題ではある

// foo.ts
import { bar } from './bar';

export function foo() {
  return bar() ? 'bar' : 'baz';
}
// bar.ts
export function bar() {
  return false;
}
// foo.test.ts
import { bar } from './bar';
import { foo } from './foo';

// fromの部分と合わせる。pathを@とかにしてる時もそれに合わせる
jest.mock('./bar');
const mockBar = bar as jest.MockedFunction<typeof bar>;
mockBar.mockReturnValue(true);

describe(foo, () => {
  beforeEach(() => {
    mockBar.mockClear();
  });
  it('should be bar', () => {
    // bar()の返却値をtrueにする
    mockBar.mockReturnValueOnce(true);
    expect(foo()).toBe('bar');
  });
  it('should be baz', () => {
    // bar()の返却値をfalseにする
    mockBar.mockReturnValueOnce(false);
    expect(foo()).toBe('baz');
  });
});
白雀白雀

Reactで、検索欄の入力の候補を非同期で引っ張りたいが、debounceを使って少しディレイをかけたい…
そんなときのテスト

import { act, renderHook } from '@testing-library/react-hooks';
import { useAutoComplete, UseAutoCompleteProps } from './useAutoComplete';

const mockFetcher = jest.fn((query: string) =>
  Promise.resolve(['入力候補A', '入力候補B']),
);

describe(useAutoComplete, () => {
  beforeEach(() => {
    mockFetcher.mockClear();
    // jest.runTimersToTimeで時間経過を再現するために必要
    jest.useFakeTimers('modern');
  });
  it('入力値が変わった後, 200ms待ってからfetchを行う', () => {
    const { rerender, result } = renderHook((props) => useAutoComplete(props), {
      initialProps: {
        keyword: '',
        fetcher: mockFetcher,
        debounceDelayTime: 200,
      } as UseAutoCompleteProps,
    });
    expect(mockFetcher).toHaveBeenCalledTimes(0);
    rerender({
      keyword: 'あ',
      fetcher: mockFetcher,
      debounceDelayTime: 200,
    });
    // 入力が変わった直後に呼ばれていないことを確認する
    expect(mockFetcher).toHaveBeenCalledTimes(0);
    // act & jest.runTimersToTimeで時間経過を再現できる
    act(() => jest.runTimersToTime(200 - 1));
    expect(mockFetcher).toHaveBeenCalledTimes(0);
    act(() => jest.runTimersToTime(1));
    // ディレイ時間経過後、入力欄の値をもとにFetchが動くことを確認する
    expect(mockFetcher).toHaveBeenCalledTimes(1);
    expect(mockFetcher).toHaveBeenCalledWith('あ');
  });
});