モック関数
モック関数は「スパイ」とも呼ばれます。これは、出力のみをテストするのではなく、他のコードによって間接的に呼び出される関数の動作をスパイできるためです。モック関数は jest.fn()
で作成できます。実装が指定されていない場合、モック関数は呼び出されると undefined
を返します。
このページの TypeScript の例は、Jest API を明示的にインポートした場合にのみ、ドキュメントどおりに動作します。
import {expect, jest, test} from '@jest/globals';
TypeScript で Jest をセットアップする方法の詳細については、入門ガイドを参照してください。
メソッド
- リファレンス
mockFn.getMockName()
mockFn.mock.calls
mockFn.mock.results
mockFn.mock.instances
mockFn.mock.contexts
mockFn.mock.lastCall
mockFn.mockClear()
mockFn.mockReset()
mockFn.mockRestore()
mockFn.mockImplementation(fn)
mockFn.mockImplementationOnce(fn)
mockFn.mockName(name)
mockFn.mockReturnThis()
mockFn.mockReturnValue(value)
mockFn.mockReturnValueOnce(value)
mockFn.mockResolvedValue(value)
mockFn.mockResolvedValueOnce(value)
mockFn.mockRejectedValue(value)
mockFn.mockRejectedValueOnce(value)
mockFn.withImplementation(fn, callback)
- 置き換えられたプロパティ
- TypeScript の使用法
リファレンス
mockFn.getMockName()
.mockName()
の呼び出しによって設定されたモック名文字列を返します。
mockFn.mock.calls
このモック関数に対して行われたすべての呼び出しの呼び出し引数を含む配列。配列の各項目は、呼び出し中に渡された引数の配列です。
たとえば、引数 f('arg1', 'arg2')
で 2 回、その後引数 f('arg3', 'arg4')
で 2 回呼び出されたモック関数 f
は、次のような mock.calls
配列を持つことになります。
[
['arg1', 'arg2'],
['arg3', 'arg4'],
];
mockFn.mock.results
このモック関数に対して行われたすべての呼び出しの結果を含む配列。この配列の各エントリは、type
プロパティと value
プロパティを含むオブジェクトです。type
は次のいずれかになります。
'return'
- 呼び出しが正常に返されることで完了したことを示します。'throw'
- 呼び出しが値をスローすることで完了したことを示します。'incomplete'
- 呼び出しがまだ完了していないことを示します。これは、モック関数自体の中、またはモックによって呼び出された関数の中から結果をテストする場合に発生します。
value
プロパティには、スローされた値または返された値が含まれます。type === 'incomplete'
の場合、value
は未定義になります。
たとえば、'result1'
を返して 3 回呼び出され、エラーをスローしてから 'result2'
を返すモック関数 f
は、次のような mock.results
配列を持つことになります。
[
{
type: 'return',
value: 'result1',
},
{
type: 'throw',
value: {
/* Error instance */
},
},
{
type: 'return',
value: 'result2',
},
];
mockFn.mock.instances
このモック関数から new
を使用してインスタンス化されたすべてのオブジェクト インスタンスを含む配列。
たとえば、2 回インスタンス化されたモック関数は、次の mock.instances
配列を持つことになります。
const mockFn = jest.fn();
const a = new mockFn();
const b = new mockFn();
mockFn.mock.instances[0] === a; // true
mockFn.mock.instances[1] === b; // true
mockFn.mock.contexts
モック関数のすべての呼び出しのコンテキストを含む配列。
コンテキストとは、関数が呼び出されたときに受け取る this
値のことです。コンテキストは、Function.prototype.bind
、Function.prototype.call
、または Function.prototype.apply
を使用して設定できます。
例
const mockFn = jest.fn();
const boundMockFn = mockFn.bind(thisContext0);
boundMockFn('a', 'b');
mockFn.call(thisContext1, 'a', 'b');
mockFn.apply(thisContext2, ['a', 'b']);
mockFn.mock.contexts[0] === thisContext0; // true
mockFn.mock.contexts[1] === thisContext1; // true
mockFn.mock.contexts[2] === thisContext2; // true
mockFn.mock.lastCall
このモック関数に対して行われた最後の呼び出しの呼び出し引数を含む配列。関数が呼び出されなかった場合、undefined
を返します。
たとえば、引数 f('arg1', 'arg2')
で 2 回、その後引数 f('arg3', 'arg4')
で 2 回呼び出されたモック関数 f
は、次のような mock.lastCall
配列を持つことになります。
['arg3', 'arg4'];
mockFn.mockClear()
mockFn.mock.calls
、mockFn.mock.instances
、mockFn.mock.contexts
、および mockFn.mock.results
配列に格納されているすべての情報をクリアします。これは、2 つのアサーションの間でモックの使用状況データをクリーンアップする場合によく役立ちます。
clearMocks
構成オプションを使用すると、各テストの前にモックを自動的にクリアできます。
mockFn.mockClear()
は、mockFn.mock
を置き換えるものであり、そのプロパティの値をリセットするだけではないことに注意してください。したがって、一時的かどうかにかかわらず、mockFn.mock
を他の変数に代入することは避けて、古いデータにアクセスしないようにする必要があります。
mockFn.mockReset()
mockFn.mockClear()
が行うすべての処理に加え、モックの実装を空の関数で置き換え、undefined
を返します。
resetMocks
構成オプションを使用すると、各テストの前にモックを自動的にリセットできます。
mockFn.mockRestore()
mockFn.mockReset()
が行うすべての処理に加え、元の (モックされていない) 実装を復元します。
これは、特定のテスト ケースで関数をモックし、他のケースで元の実装を復元する場合に役立ちます。
restoreMocks
構成オプションを使用すると、各テストの前にモックを自動的に復元できます。
mockFn.mockRestore()
は、モックが jest.spyOn()
で作成された場合にのみ機能します。したがって、jest.fn()
を手動で割り当てる場合は、自分で復元する必要があります。
mockFn.mockImplementation(fn)
モックの実装として使用する必要がある関数を受け入れます。モック自体は、入ってくるすべての呼び出しと、そこから発生するインスタンスをすべて記録します。唯一の違いは、モックが呼び出されたときにも実装が実行されることです。
jest.fn(implementation)
は jest.fn().mockImplementation(implementation)
の省略形です。
- JavaScript
- TypeScript
const mockFn = jest.fn(scalar => 42 + scalar);
mockFn(0); // 42
mockFn(1); // 43
mockFn.mockImplementation(scalar => 36 + scalar);
mockFn(2); // 38
mockFn(3); // 39
import {jest} from '@jest/globals';
const mockFn = jest.fn((scalar: number) => 42 + scalar);
mockFn(0); // 42
mockFn(1); // 43
mockFn.mockImplementation(scalar => 36 + scalar);
mockFn(2); // 38
mockFn(3); // 39
.mockImplementation()
は、クラス コンストラクターをモックするためにも使用できます
- JavaScript
- TypeScript
module.exports = class SomeClass {
method(a, b) {}
};
const SomeClass = require('./SomeClass');
jest.mock('./SomeClass'); // this happens automatically with automocking
const mockMethod = jest.fn();
SomeClass.mockImplementation(() => {
return {
method: mockMethod,
};
});
const some = new SomeClass();
some.method('a', 'b');
console.log('Calls to method:', mockMethod.mock.calls);
export class SomeClass {
method(a: string, b: string): void {}
}
import {jest} from '@jest/globals';
import {SomeClass} from './SomeClass';
jest.mock('./SomeClass'); // this happens automatically with automocking
const mockMethod = jest.fn<(a: string, b: string) => void>();
jest.mocked(SomeClass).mockImplementation(() => {
return {
method: mockMethod,
};
});
const some = new SomeClass();
some.method('a', 'b');
console.log('Calls to method:', mockMethod.mock.calls);
mockFn.mockImplementationOnce(fn)
モック関数の 1 回の呼び出しに対して、モックの実装として使用される関数を受け入れます。複数の関数呼び出しで異なる結果が生成されるように、チェーンすることができます。
- JavaScript
- TypeScript
const mockFn = jest
.fn()
.mockImplementationOnce(cb => cb(null, true))
.mockImplementationOnce(cb => cb(null, false));
mockFn((err, val) => console.log(val)); // true
mockFn((err, val) => console.log(val)); // false
import {jest} from '@jest/globals';
const mockFn = jest
.fn<(cb: (a: null, b: boolean) => void) => void>()
.mockImplementationOnce(cb => cb(null, true))
.mockImplementationOnce(cb => cb(null, false));
mockFn((err, val) => console.log(val)); // true
mockFn((err, val) => console.log(val)); // false
モック関数が .mockImplementationOnce()
で定義された実装を使い果たすと、jest.fn(() => defaultValue)
または .mockImplementation(() => defaultValue)
で設定された既定の実装を実行します(呼び出された場合)。
const mockFn = jest
.fn(() => 'default')
.mockImplementationOnce(() => 'first call')
.mockImplementationOnce(() => 'second call');
mockFn(); // 'first call'
mockFn(); // 'second call'
mockFn(); // 'default'
mockFn(); // 'default'
mockFn.mockName(name)
どのモック関数が参照されているかを示すために、テスト結果出力で 'jest.fn()'
の代わりに使用する文字列を受け入れます。
例
const mockFn = jest.fn().mockName('mockedFunction');
// mockFn();
expect(mockFn).toHaveBeenCalled();
これにより、次のエラーが発生します
expect(mockedFunction).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
mockFn.mockReturnThis()
の省略形
jest.fn(function () {
return this;
});
mockFn.mockReturnValue(value)
の省略形
jest.fn().mockImplementation(() => value);
モック関数が呼び出されるたびに返される値を受け入れます。
- JavaScript
- TypeScript
const mock = jest.fn();
mock.mockReturnValue(42);
mock(); // 42
mock.mockReturnValue(43);
mock(); // 43
import {jest} from '@jest/globals';
const mock = jest.fn<() => number>();
mock.mockReturnValue(42);
mock(); // 42
mock.mockReturnValue(43);
mock(); // 43
mockFn.mockReturnValueOnce(value)
の省略形
jest.fn().mockImplementationOnce(() => value);
モック関数への1回の呼び出しに対して返される値を設定します。連鎖させて、モック関数への連続した呼び出しが異なる値を返すようにできます。使用するmockReturnValueOnce
値がなくなると、呼び出しはmockReturnValue
で指定された値を返します。
- JavaScript
- TypeScript
const mockFn = jest
.fn()
.mockReturnValue('default')
.mockReturnValueOnce('first call')
.mockReturnValueOnce('second call');
mockFn(); // 'first call'
mockFn(); // 'second call'
mockFn(); // 'default'
mockFn(); // 'default'
import {jest} from '@jest/globals';
const mockFn = jest
.fn<() => string>()
.mockReturnValue('default')
.mockReturnValueOnce('first call')
.mockReturnValueOnce('second call');
mockFn(); // 'first call'
mockFn(); // 'second call'
mockFn(); // 'default'
mockFn(); // 'default'
mockFn.mockResolvedValue(value)
の省略形
jest.fn().mockImplementation(() => Promise.resolve(value));
非同期テストで非同期関数をモックするのに役立ちます。
- JavaScript
- TypeScript
test('async test', async () => {
const asyncMock = jest.fn().mockResolvedValue(43);
await asyncMock(); // 43
});
import {jest, test} from '@jest/globals';
test('async test', async () => {
const asyncMock = jest.fn<() => Promise<number>>().mockResolvedValue(43);
await asyncMock(); // 43
});
mockFn.mockResolvedValueOnce(value)
の省略形
jest.fn().mockImplementationOnce(() => Promise.resolve(value));
複数の非同期呼び出しで異なる値を解決するのに役立ちます。
- JavaScript
- TypeScript
test('async test', async () => {
const asyncMock = jest
.fn()
.mockResolvedValue('default')
.mockResolvedValueOnce('first call')
.mockResolvedValueOnce('second call');
await asyncMock(); // 'first call'
await asyncMock(); // 'second call'
await asyncMock(); // 'default'
await asyncMock(); // 'default'
});
import {jest, test} from '@jest/globals';
test('async test', async () => {
const asyncMock = jest
.fn<() => Promise<string>>()
.mockResolvedValue('default')
.mockResolvedValueOnce('first call')
.mockResolvedValueOnce('second call');
await asyncMock(); // 'first call'
await asyncMock(); // 'second call'
await asyncMock(); // 'default'
await asyncMock(); // 'default'
});
mockFn.mockRejectedValue(value)
の省略形
jest.fn().mockImplementation(() => Promise.reject(value));
常にリジェクトする非同期モック関数を作成するのに役立ちます。
- JavaScript
- TypeScript
test('async test', async () => {
const asyncMock = jest
.fn()
.mockRejectedValue(new Error('Async error message'));
await asyncMock(); // throws 'Async error message'
});
import {jest, test} from '@jest/globals';
test('async test', async () => {
const asyncMock = jest
.fn<() => Promise<never>>()
.mockRejectedValue(new Error('Async error message'));
await asyncMock(); // throws 'Async error message'
});
mockFn.mockRejectedValueOnce(value)
の省略形
jest.fn().mockImplementationOnce(() => Promise.reject(value));
.mockResolvedValueOnce()
と組み合わせて使用したり、複数の非同期呼び出しで異なる例外でリジェクトしたりするのに役立ちます。
- JavaScript
- TypeScript
test('async test', async () => {
const asyncMock = jest
.fn()
.mockResolvedValueOnce('first call')
.mockRejectedValueOnce(new Error('Async error message'));
await asyncMock(); // 'first call'
await asyncMock(); // throws 'Async error message'
});
import {jest, test} from '@jest/globals';
test('async test', async () => {
const asyncMock = jest
.fn<() => Promise<string>>()
.mockResolvedValueOnce('first call')
.mockRejectedValueOnce(new Error('Async error message'));
await asyncMock(); // 'first call'
await asyncMock(); // throws 'Async error message'
});
mockFn.withImplementation(fn, callback)
コールバックが実行されている間、一時的にモックの実装として使用される関数を受け入れます。
test('test', () => {
const mock = jest.fn(() => 'outside callback');
mock.withImplementation(
() => 'inside callback',
() => {
mock(); // 'inside callback'
},
);
mock(); // 'outside callback'
});
mockFn.withImplementation
は、コールバックが非同期(thenable
を返す)であるかどうかにかかわらず使用できます。コールバックが非同期の場合、Promiseが返されます。Promiseを待機すると、コールバックが待機され、実装がリセットされます。
test('async test', async () => {
const mock = jest.fn(() => 'outside callback');
// We await this call since the callback is async
await mock.withImplementation(
() => 'inside callback',
async () => {
mock(); // 'inside callback'
},
);
mock(); // 'outside callback'
});
置き換えられたプロパティ
replacedProperty.replaceValue(value)
既に置き換えられたプロパティの値を変更します。これは、プロパティを置き換えてから、特定のテストで値を調整する場合に役立ちます。代替として、同じプロパティに対してjest.replaceProperty()
を複数回呼び出すことができます。
replacedProperty.restore()
オブジェクトのプロパティを元の値に戻します。
replacedProperty.restore()
は、プロパティ値がjest.replaceProperty()
で置き換えられた場合にのみ機能することに注意してください。
restoreMocks
構成オプションを使用すると、各テストの前に置き換えられたプロパティを自動的に復元できます。
TypeScriptの使用法
このページの TypeScript の例は、Jest API を明示的にインポートした場合にのみ、ドキュメントどおりに動作します。
import {expect, jest, test} from '@jest/globals';
TypeScript で Jest をセットアップする方法の詳細については、入門ガイドを参照してください。
jest.fn(implementation?)
実装がjest.fn()
に渡された場合、正しいモック型が推論されます。実装が省略されるユースケースはたくさんあります。型安全性を確保するには、ジェネリック型引数を渡すことができます(詳細については上記の例も参照してください)。
import {expect, jest, test} from '@jest/globals';
import type add from './add';
import calculate from './calc';
test('calculate calls add', () => {
// Create a new mock that can be used in place of `add`.
const mockAdd = jest.fn<typeof add>();
// `.mockImplementation()` now can infer that `a` and `b` are `number`
// and that the returned value is a `number`.
mockAdd.mockImplementation((a, b) => {
// Yes, this mock is still adding two numbers but imagine this
// was a complex function we are mocking.
return a + b;
});
// `mockAdd` is properly typed and therefore accepted by anything
// requiring `add`.
calculate(mockAdd, 1, 2);
expect(mockAdd).toHaveBeenCalledTimes(1);
expect(mockAdd).toHaveBeenCalledWith(1, 2);
});
jest.Mock<T>
モック関数の型を構築します。たとえば、jest.fn()
の戻り値の型です。再帰的なモック関数を定義する必要がある場合に役立ちます。
import {jest} from '@jest/globals';
const sumRecursively: jest.Mock<(value: number) => number> = jest.fn(value => {
if (value === 0) {
return 0;
} else {
return value + fn(value - 1);
}
});
jest.Mocked<Source>
jest.Mocked<Source>
ユーティリティ型は、Jestモック関数の型定義でラップされたSource
型を返します。
import {expect, jest, test} from '@jest/globals';
import type {fetch} from 'node-fetch';
jest.mock('node-fetch');
let mockedFetch: jest.Mocked<typeof fetch>;
afterEach(() => {
mockedFetch.mockClear();
});
test('makes correct call', () => {
mockedFetch = getMockedFetch();
// ...
});
test('returns correct data', () => {
mockedFetch = getMockedFetch();
// ...
});
クラス、関数、またはオブジェクトの型を、jest.Mocked<Source>
に型引数として渡すことができます。入力型を制約したい場合は、jest.MockedClass<Source>
、jest.MockedFunction<Source>
、またはjest.MockedObject<Source>
を使用してください。
jest.Replaced<Source>
jest.Replaced<Source>
ユーティリティ型は、Jestの置き換えられたプロパティの型定義でラップされたSource
型を返します。
export function isLocalhost(): boolean {
return process.env['HOSTNAME'] === 'localhost';
}
import {afterEach, expect, it, jest} from '@jest/globals';
import {isLocalhost} from '../utils';
let replacedEnv: jest.Replaced<typeof process.env> | undefined = undefined;
afterEach(() => {
replacedEnv?.restore();
});
it('isLocalhost should detect localhost environment', () => {
replacedEnv = jest.replaceProperty(process, 'env', {HOSTNAME: 'localhost'});
expect(isLocalhost()).toBe(true);
});
it('isLocalhost should detect non-localhost environment', () => {
replacedEnv = jest.replaceProperty(process, 'env', {HOSTNAME: 'example.com'});
expect(isLocalhost()).toBe(false);
});
jest.mocked(source, options?)
mocked()
ヘルパーメソッドは、source
オブジェクトとその深くネストされたメンバーの型を、Jestモック関数の型定義でラップします。深くモックされた動作を無効にするために、options
引数として{shallow: true}
を渡すことができます。
source
オブジェクトを返します。
export const song = {
one: {
more: {
time: (t: number) => {
return t;
},
},
},
};
import {expect, jest, test} from '@jest/globals';
import {song} from './song';
jest.mock('./song');
jest.spyOn(console, 'log');
const mockedSong = jest.mocked(song);
// or through `jest.Mocked<Source>`
// const mockedSong = song as jest.Mocked<typeof song>;
test('deep method is typed correctly', () => {
mockedSong.one.more.time.mockReturnValue(12);
expect(mockedSong.one.more.time(10)).toBe(12);
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
});
test('direct usage', () => {
jest.mocked(console.log).mockImplementation(() => {
return;
});
console.log('one more time');
expect(jest.mocked(console.log).mock.calls).toHaveLength(1);
});
jest.Spied<Source>
スパイされたクラスまたは関数の型を構築します(つまり、jest.spyOn()
の戻り値の型)。
import {jest} from '@jest/globals';
export function setDateNow(now: number): jest.Spied<typeof Date.now> {
return jest.spyOn(Date, 'now').mockReturnValue(now);
}
import {afterEach, expect, type jest, test} from '@jest/globals';
import {setDateNow} from './__utils__/setDateNow';
let spiedDateNow: jest.Spied<typeof Date.now> | undefined = undefined;
afterEach(() => {
spiedDateNow?.mockReset();
});
test('renders correctly with a given date', () => {
spiedDateNow = setDateNow(1_482_363_367_071);
// ...
expect(spiedDateNow).toHaveBeenCalledTimes(1);
});
クラスまたは関数の型を、jest.Spied<Source>
に型引数として渡すことができます。入力型を制約したい場合は、jest.SpiedClass<Source>
またはjest.SpiedFunction<Source>
を使用してください。
スパイされたゲッターまたはセッターの型を作成するには、それぞれjest.SpiedGetter<Source>
またはjest.SpiedSetter<Source>
を使用します。