検索条件
お知らせ
現在サイトのリニューアル作業中のため、表示が崩れているページが存在することがあります。
基本出来ないのでwindow.location.hrefを操作するためのラッパーを作って、それをjest.spyOn()して解決する
理由はjsdomが対応していないため
 
 
  
global.navigatorをモックにする方法
 
jestのモック機能はプロパティのモックが出来ないので、実オブジェクトを強制的に書き換えて実施する 
Object.defineProperty() を利用して実装
value, プロパティが返す値 
configurable, 再定義可能かどうか、設定しないと再実行でコケる 
 
- 実際の使用ではユーティリティ関数を作っておき、 
afterAll()でnavigator.userAgentを初期値に戻すのが望ましい 
Object.defineProperty(global.navigator, 'userAgent', {
  value:
      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36',
  configurable: true,
});
 
 
  
- 理論上可能そうだったので、ひたすら試行錯誤してたらできたのでメモがてら
 
axiosそのものをモックして、その結果に応じた処理が想定通り走っているかどうかを確認したい 
create-react-appで作成したTypeScript Reactのプロジェクトにnpm tを掛けてテストすることを想定 
- 但し検証用のプロジェクトは@lycolia/ts-boilerplate-generator-cliで作成
 
	| Env | 
	Ver | 
	| react | 
	17.0.1 | 
	| react-scripts | 
	4.0.2 | 
	| typescript | 
	4.1.3 | 
 
- ページ読み込み時に一回だけAPIを蹴ってなんかすることを想定
 
import axios from 'axios';
import { useEffect } from 'react';
export const AsyncHookExample = () => {
  useEffect(() => {
    axios
      .get('https://localhost/')
      .then((res) => console.log(res))
      .catch((err) => console.error(err));
  }, []);
  return <p />;
};
axiosをモックした上で、モック関数のPromiseの解決を待ち、 PromiseのコールバックをspyOnして検査する内容 
- モック関数の
Promiseの解決法を求めるのにハマりましたが、これはfoo.mock.results[0].valueに対してawait expect(foo.mock.results[0].value).resolvesとしてやればいける 
- 細かいことはソースコードのコメント参照
 
import { render } from '@testing-library/react';
import axios from 'axios';
import { AsyncHookExample } from './AsyncHookExample';
// `axios` をモックにする
jest.mock('axios');
// `axios` のモックを取得
const mockedAxios = axios as jest.Mocked<typeof axios>;
// `console.log()` を `spyOn'
const spiedConsoleLog = jest.spyOn(console, 'log');
// `console.error()` を `spyOn'
const spiedConsoleError = jest.spyOn(console, 'error');
describe('AsyncHookExample', () => {
  it('resolve promise in Hook', async () => {
    // テストに期待する結果
    // `axios` はモックなので値は適当
    const beResult = {
      status: 200,
      data: null,
    };
    // `axios` のモックが `resolve` する値を設定
    mockedAxios.get.mockResolvedValue(beResult);
    // コンポーネントを `render` して `useEffect` を走らせる
    render(<AsyncHookExample />);
    // `axios.get()` が呼ばれたことを確認
    expect(mockedAxios.get).toBeCalled();
    // モックの結果を取得
    const testResult = mockedAxios.get.mock.results[0].value;
    // `reject` された値が期待通りであることを確認
    await expect(testResult).resolves.toEqual(beResult);
    // `useEffect` の中の `Promise` の中にある `console.log()` が呼ばれたことを確認
    expect(spiedConsoleLog).toBeCalled();
  });
  it('reject promise in Hook', async () => {
    // テストに期待する結果
    // `axios` はモックなので値は適当
    const beResult = {
      status: 400,
      data: null,
    };
    // `axios` のモックが `reject` する値を設定
    mockedAxios.get.mockRejectedValue(beResult);
    // コンポーネントを `render` して `useEffect` を走らせる
    render(<AsyncHookExample />);
    // `axios.get()` が呼ばれたことを確認
    expect(mockedAxios.get).toBeCalled();
    // モックの結果を取得
    const testResult = mockedAxios.get.mock.results[0].value;
    // `reject` された値が期待通りであることを確認
    await expect(testResult).rejects.toEqual(beResult);
    // `useEffect` の中の `Promise` の中にある `console.error()` が呼ばれたことを確認
    expect(spiedConsoleError).toBeCalled();
  });
});
mockResolvedValueのスコープやらモックのPromiseを解決させるマッチャやら調べるのに手間取った
- あとはそもそも
Promiseが解決した結果がどこに入るのかとか、兎に角あれこれ 
 
expect(mockedAxios.get).toBeCalled()が通るのはすぐに気づいたので、なら出来るだろうとひたすら調べてた
 
 
 
  
historyのバージョンとreact-router-domのバージョンが上手く噛み合ってないと既存実装で型エラーが出る可能性がある 
	| Env | 
	Ver | 
	| react | 
	17.0.2 | 
	| react-dom | 
	17.0.2 | 
	| react-router-dom | 
	5.2.0 | 
	| react-scripts | 
	4.0.3 | 
	| typescript | 
	4.2.3 | 
	| history | 
	4.10.1 | 
 
ToPageに飛ばしたlocation.stateを検査する内容 
export const ToPage = () => {
  const location = useLocation<{ test?: string }>();
  return <p>from {location.state?.test ?? 'nothing'}</p>;
};
const renderWithRouter = (ui: JSX.Element, { route = '/' } = {}) => {
  window.history.pushState({}, 'Test page', route);
  return render(ui, { wrapper: BrowserRouter });
};
describe('onload', () => {
  it('from history.push', () => {
    const history = createMemoryHistory<{ test: string }>();
    history.push(AppRoute.to.path, { test: 'foo' });
    const { container } = renderWithRouter(
      <Router history={history}>
        <ToPage />
      </Router>
    );
    expect(container).toHaveTextContent('from foo');
    expect(history.location.state.test).toBe('foo');
  });
});
 
 
  
- まず
history.push('/path/to/dest')自体のテストを書くことはないはずなので、何かしらの副作用として実行される事が前提です 
- testing-library.com に書いてあることの応用です
 
/**
 * useHistory() を使えるようにするテストユーティリティ
 */
const renderWithRouter = (children: JSX.Element, { route = '/' } = {}) => {
  window.history.pushState({}, 'Test page', route);
  return render(children, {
    wrapper: BrowserRouter,
  });
};
// spy 用のモック関数
const mockPush = jest.fn();
// react-router-dom をモックにする
jest.mock('react-router-dom', () => ({
  useHistory: () => ({
    // モック関数を設定
    push: mockPush,
  }),
}));
describe('Bar', () => {
  it('history.push のテスト', () => {
    renderWithRouter(<Bar />, {
      route: '/foo/bar',
    });
    // ここらへんに同期的に副作用を起こす処理を書く
    // 以降、副作用で history.push('/foo/baz') されたとする
    // 呼ばれたことの確認
    expect(mockPush).toBeCalled();
    // 引数の確認
    expect(mockPush.mock.calls[0][0]).toBe('/foo/baz');
  });
});