💡
Jestのモックパターン
Jest でモックする方法が色々あって毎回調べることになっているのでまとめておく
なお clearMocks
オプションに true
が設定されている前提です
副作用を止めるだけ
例えば以下
src/utils.ts
export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
src/index.ts
import { sleep } from './utils';
export const test = async () => {
await sleep(1000);
// ...
return 1;
};
戻り値のいらない sleep
のような関数の副作用を止める場合はモジュールごとモックするだけで良い
__tests__/index.ts
import { test } from '../src';
// モジュールごとモック
jest.mock('../utils');
describe('test', () => {
it('should return 1', async () => {
expect(await test()).toBe(1);
})
});
テスト全体で同じ動作
例えば以下
src/calc.ts
export const sum = (a: number, b: number) => a + b;
src/index.ts
import { sum } from './calc';
export const sumAll = (...values: Array<number>): number => {
return values.reduce(sum, 0);
};
sum
の動作をすべてのテストで同じ動作でモックする場合は以下のようにすれば良い
__tests__/index.ts
import { sumAll } from '../src';
// モジュールごとモック
jest.mock('../src/calc', () => ({
sum: (a: number, b: number) => a + b + 1, // 動作を定義
}));
describe('sumAll', async () => {
it('should return sum value', () => {
expect(sumAll(1, 2, 3)).toBe(9); // 別の動作でモックしたので6ではなく9
});
});
src/calc.ts
に他の関数も存在し、それらはそのまま使用してほしい場合は requireActual
を使えば良い
__tests__/index.ts
// モジュールごとモック
jest.mock('../src/calc', () => ({
...jest.requireActual<{}>('../src/calc'),
sum: (a: number, b: number) => a + b + 1,
}));
テストごとに違う動作
前述の例で sum
の動作をテストごとに異なる動作でモックする場合は以下のようにすれば良い
__tests__/index.ts
import { sumAll } from '../src';
import * as calc from '../src/calc';
describe('sumAll', async () => {
it('should return sum value 1', () => {
expect(sumAll(1, 2, 3)).toBe(6); // 元の動作のまま
});
it('should return sum value 2', () => {
jest.spyOn(calc, 'sum').mockReturnValue(1); // sum は 1 を返す関数としてモック
expect(sumAll(1, 2, 3)).toBe(1);
});
it('should return sum value 3', () => {
jest.spyOn(calc, 'sum').mockImplementation((a, b) => a + b + 2); // 別の動作でモック
expect(sumAll(1, 2, 3)).toBe(12);
});
});
または以下
__tests__/index.ts
import { sumAll } from '../src';
import { sum } from '../src/calc';
jest.mock('../src/calc');
describe('sumAll', async () => {
it('should return sum value 1', () => {
(sum as jest.Mock).mockReturnValue(1); // sum は 1 を返す関数としてモック
expect(sumAll(1, 2, 3)).toBe(1);
});
it('should return sum value 2', () => {
(sum as jest.Mock).mockImplementation((a, b) => a + b + 2); // 別の動作でモック
expect(sumAll(1, 2, 3)).toBe(12);
});
});
関数呼び出しをテスト
前述の例で sum
の呼ばれた回数や呼ばれたときの引数を確認したい場合は以下のようにすれば良い
__tests__/index.ts
import { sumAll } from '../src';
import * as calc from '../src/calc';
describe('sumAll', async () => {
it('should return sum value', () => {
const sum = jest.fn(() => 1);
jest.spyOn(calc, 'sum').mockImplementation(sum);
expect(sumAll(1, 2, 3)).toBe(1);
expect(sum).toBeCalledTimes(3); // 3回呼ばれたことを確認
expect(sum).lastCalledWith(1, 3, 2, [1, 2, 3]); // a = 1, b, index, original array
});
});
Discussion