from 非同期なReactの状態更新をテストする useStateを差し替える

コンポーネントの中でsetValueしている場合に、これがactで包まれていないと警告される

output

console.error
    Warning: An update to MyAsyncComponent inside a test was not wrapped in act(...).
    When testing, code that causes React state updates should be wrapped into act(...):
...
      15 |       console.log(6);
    > 16 |       setValue(x);
         |       ^

簡単な解決策としては「何もしないact実装を用意して本体コード中でそれを使っておき、テストの時だけ本物に差し替える」という案もあるが、なんとなく嫌なので useStateを差し替える。

MockUseState.ts

import React, { Dispatch, useCallback } from "react";
import { act } from "@testing-library/react";
import { useState as originalUseState } from "react";
 
export const mockUseState = () => {
  return jest.spyOn(React, "useState").mockImplementation((arg?: unknown): [
    unknown,
    Dispatch<unknown>
  ] => {
    const [s, dispatch] = originalUseState(arg);
    const wrappedDispatch = useCallback(
      (arg: unknown): void => {
        act(() => {
          dispatch(arg);
        });
      },
      [dispatch]
    );
    return [s, wrappedDispatch];
  });
};

updated 2021-03-12

test.tsx

let m_mockUseState: jest.SpyInstance<[unknown, React.Dispatch<unknown>], []>;
beforeEach(() => {
  m_mockUseState = mockUseState();
});
afterEach(() => {
  m_mockUseState.mockRestore();
});