お知らせ

現在サイトのリニューアル作業中のため、全体的にページの表示が乱れています。
  • まず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');
  });
});
投稿日:
開発::テスト言語::TypeScriptNode.js::Jest
  • fsとかchild_processとかそういうやつをモックする方法
    • jest単体の機能では実現できないのでハマるポイント
  • たぶん jest.MockedFunction でも同じことができる

サンプルコード

import { execSync } from 'child_process';

// まず全体をモックする
jest.mock('child_process');

// モックにする
const mockExecSync = execSync as jest.MockedFunction<typeof execSync>;

describe('execSync', () => {
  it('spyOn execSync', () => {
    // 対象をコール
    execSync('echo 1');
    // 通る
    expect(mockExecSync).toHaveBeenCalled();
    // 通る, echo 2とかにするとちゃんとコケてくれる
    expect(mockExecSync).toHaveBeenCalledWith('echo 1');
  });
});
投稿日:
Node.js::Jest開発::テスト言語::TypeScript

スプレッド演算子で引き渡した内容の内、一部だけを利用するケースを想定

サンプルコード

検査対象

type FooBar = {
  foo: string;
  bar: number;
};

/**
 * `foo` と `bar` しか使われない関数
 * @param arg
 */
export const spreadExample = (arg: FooBar) => {
  return new Promise((resolve) => {
    resolve([arg.foo, arg.bar]);
  });
};

テストコード

import * as spex from '../spreadExample';

const spiedSpreadExample = jest.spyOn(spex, 'spreadExample');

describe('spreadExample', () => {
  it('example', async () => {
    const test = {
      foo: 'hoge',
      bar: 123,
      baz: true, // 今回の課題となる項目
    };
    // 余計な `baz` が流し込まれているため、
    // 普通に検査しようとすると `baz` が邪魔で上手く行かないケース
    await spex.spreadExample({ ...test });

    expect(spiedSpreadExample).toBeCalled();
    // コールバックで `expect.objectContaining()` を呼ぶことにより
    // 見たい項目だけを検査することが可能(`baz` を見なくて済む
    expect(spiedSpreadExample).toHaveBeenCalledWith(
      expect.objectContaining({
        foo: 'hoge',
        bar: 123,
      })
    );
  });
});
投稿日:
言語::TypeScript開発::テストNode.js::Jest

単体テストの観点でいうとprocess.argvそのものはモックしないほうが良いと思っているので、本記事ではprocess.argv自体をモックする方法と、それを回避する方法を紹介する

サンプルコード

正攻法

describe('example', () => {
  afterEach(() => {
    // 元に戻しておく
    process.argv.length = 2;
  });
  it('got first param', () => {
    process.argv.push('param1');
    expect(process.argv.length).toBe(3);
    expect(process.argv[2]).toBe('param1');
  });
});

そもそもモックせずに済むように設計する

process.argvを利用している関数でprocess.argvを引数に取ればモック不要となる

const getArgv = (argv: string[]) => {
    // some procedure
}

describe('getArgv', () => {
  it('got first param', () => {
    const argv = [,, 'param1'];
    expect(argv.length).toBe(3);
    expect(argv[2]).toBe('param1');
  });
});