Jest mockImplementationの使い方|戻り値・非同期・実装差し替えを実務目線で解説
mockImplementation は、Jestのモック関数に任意の中身を差し込むためのメソッドです。戻り値を固定するだけなら mockReturnValue で足りますが、引数によって振る舞いを変えたい、条件分岐やエラーを再現したいといった場面では mockImplementation が必要になります。この記事ではJest 30.4の公式仕様に沿って、mockImplementation と mockImplementationOnce を軸に、戻り値設定・非同期・jest.spyOn・jest.mock・requireActual、そしてテスト間の後始末(mockClear/mockReset/mockRestore)までを一続きで整理します。
まとめ:目的別のモックメソッド早見
やりたいことから逆引きできるよう、主要メソッドの使い分けを先に示します。詳細は各セクションで扱います。
| やりたいこと | 使うメソッド |
|---|---|
| 毎回同じ値を返す | mockReturnValue |
| 1回だけ別の値を返す | mockReturnValueOnce |
| 非同期(resolve)を返す | mockResolvedValue / mockResolvedValueOnce |
| 非同期(reject=失敗)を返す | mockRejectedValue / mockRejectedValueOnce |
| 引数で振る舞いを変える/ロジックを差し替える | mockImplementation / mockImplementationOnce |
| 既存オブジェクトのメソッドを監視・置換する | jest.spyOn |
| モジュール全体を差し替える | jest.mock |
| モジュールの一部だけ本物を使う | jest.requireActual |
| 呼び出し履歴だけ消す/実装ごと消す/元に戻す | mockClear / mockReset / mockRestore |
結論を一言でいえば、値だけ差し替えたいなら mockReturnValue 系、ロジックを差し替えたいなら mockImplementation 系、テスト対象そのものではなくその依存先を差し替えたいなら jest.spyOn か jest.mock を使う、という役割分担になります。
Jestのモック関数とは?依存を切り離してテストを安定させる仕組み
モック関数は、テスト対象が呼び出す「外部の何か」を偽物に置き換えるための関数です。APIリクエスト、データベース、現在時刻、乱数といった、実行するたびに結果が変わったり時間がかかったりする依存を差し替えることで、テストを高速かつ決定的(毎回同じ結果)にできます。
モック・スタブ・スパイの違い
用語が混同されがちなので先に区別します。スタブは「決められた値を返すだけの偽物」、スパイは「本物の関数を呼びつつ呼び出し内容を記録するもの」、モックは「差し替えたうえで呼び出し内容を検証するもの」を指すのが一般的な使い分けです。ただしJestではこれらを厳密に分けず、jest.fn() で作るモック関数が呼び出し記録(mock.calls)を常に持つため、スパイ的にもスタブ的にも使えます。実務では「呼ばれ方を検証したい=jest.spyOn」「戻り値を差し替えたい=mockReturnValue/mockImplementation」と、目的で選べば十分です。
jest.fn()・jest.mock()・jest.spyOn()の役割分担
Jestのモックには入口が3つあり、どこを差し替えるかで選びます。
| API | 差し替える対象 | 元の実装 |
|---|---|---|
| jest.fn() | 単体のコールバック・関数 | 持たない(空の関数) |
| jest.spyOn() | 既存オブジェクトのメソッド | 保持(既定では本物を実行) |
| jest.mock() | モジュール(import先)全体 | 既定で自動モック化 |
いずれの入口で作ったモックにも、この後で扱う mockReturnValue や mockImplementation といった振る舞い設定メソッドを同じようにチェーンできます。
モック関数の戻り値設定(mockReturnValue系メソッド)
「この関数はとりあえずこの値を返すことにしたい」という最も多い要求は、戻り値設定メソッドで済みます。ロジックを書く必要がないぶん mockImplementation より読みやすく、まずはこちらを検討します。
mockReturnValueとmockReturnValueOnceの使い分け
mockReturnValue(value) は毎回同じ値を返します。mockReturnValueOnce(value) は1回の呼び出しにだけ効き、使い切ると mockReturnValue で設定した既定値に戻ります。連続する呼び出しで値を変えたいときはOnceを重ねます。
const mockFn = jest.fn();
mockFn.mockReturnValue('default')
.mockReturnValueOnce('first')
.mockReturnValueOnce('second');
mockFn(); // 'first'
mockFn(); // 'second'
mockFn(); // 'default'(Onceを使い切ったので既定値)
公式仕様上、mockReturnValue(v) は mockImplementation(() => v) の短縮形です。値を返すだけならこちらで書くほうが意図が明確になります。
非同期の戻り値:mockResolvedValue・mockRejectedValue
async/await の関数やPromiseを返す依存をモックするときは、Promiseで包む専用メソッドを使います。mockResolvedValue(v) は mockImplementation(() => Promise.resolve(v))、mockRejectedValue(e) は Promise.reject(e) の短縮形です。失敗(reject)系はエラーハンドリングのテストで使います。
const fetchUser = jest.fn();
fetchUser.mockResolvedValue({ id: 1, name: 'Taro' });
await fetchUser(); // { id: 1, name: 'Taro' }
const fetchNg = jest.fn();
fetchNg.mockRejectedValue(new Error('Network error'));
await expect(fetchNg()).rejects.toThrow('Network error');
1回目は成功、2回目は失敗、といったリトライ処理の検証には mockResolvedValueOnce と mockRejectedValueOnce を組み合わせます。実践例は後半のAPIモックの節で示します。
mockImplementationによる任意実装への差し替え
戻り値を固定するだけでは足りず、引数に応じて返す値を変えたい、副作用(コールバック呼び出しなど)を再現したい、というときが mockImplementation の出番です。ここが本記事の主題になります。
mockImplementationの基本構文
mockImplementation(fn) は、渡した関数をモックの中身として実行させます。モック関数としての記録機能(呼び出し回数・引数)はそのまま残るため、「差し替えた実装で動かしつつ、呼ばれ方も検証する」ことが同時にできます。
const calc = jest.fn();
calc.mockImplementation((a, b) => a + b);
calc(2, 3); // 5
expect(calc).toHaveBeenCalledWith(2, 3);
なお jest.fn(fn) と初期化時に関数を渡すのは、jest.fn().mockImplementation(fn) と等価です。テスト全体で同じ実装を使うなら生成時に渡し、テストケースごとに差し替えるなら後から mockImplementation を呼ぶ、と書き分けると読みやすくなります。
mockImplementationOnceによる呼び出しごとの戻り値切り替え
mockImplementationOnce(fn) は1回の呼び出しにだけ効く実装で、チェーンすると呼び出し順に別々の振る舞いを割り当てられます。Onceを使い切ると、mockImplementation で設定した既定実装(無ければ undefined を返す空実装)にフォールバックします。
const handler = jest.fn(() => 'default')
.mockImplementationOnce(() => 'A')
.mockImplementationOnce(() => 'B');
handler(); // 'A'
handler(); // 'B'
handler(); // 'default'
Node流のコールバック((err, result) => ...)を1回目は成功、2回目は失敗で呼び分ける、といったシナリオもOnceの連結で表現できます。
withImplementationによる一時的な差し替え(Jest 29.1以降)
「このコールバックの間だけ実装を変え、抜けたら元に戻す」ことを明示したいなら、Jest 29.1.0で追加された withImplementation(fn, callback) が使えます。callback の実行中だけ fn が実装として使われ、終わると自動で元へ戻ります。非同期コールバックを渡すとPromiseを返すため await できます。
const mock = jest.fn(() => 'outside');
mock.withImplementation(() => 'inside', () => {
mock(); // 'inside'
});
mock(); // 'outside'(自動で復元)
手動で mockImplementationOnce を積んで戻し忘れる事故を避けたい局所的な差し替えでは、こちらのほうが安全です。
mockReturnValueとmockImplementationの使い分け基準
迷ったら次の基準で選びます。返すのが定数なら mockReturnValue、引数を使う・条件分岐する・例外を投げる・副作用を起こすなら mockImplementation。mockReturnValue はそもそも mockImplementation(() => v) の短縮形なので、ロジック不要の場面で mockImplementation を持ち出すのは冗長です。逆に、mockReturnValue で throw を書こうとして手が止まったら mockImplementation に切り替えるサインです。
jest.spyOnによる既存メソッドの監視・置換
jest.fn() が「新しく偽物を作る」のに対し、jest.spyOn(object, method) は「既存オブジェクトのメソッドを差し替え可能な形で包む」ためのAPIです。テスト対象が直接importしているのではなく、あるオブジェクトのメソッド経由で依存を呼んでいるときに向きます。
spyOnの基本(呼び出し記録と実装維持)
jest.spyOn は既定では元の実装をそのまま実行しつつ、呼び出しを記録します。「本物を動かしたまま、呼ばれたかどうかだけ確認したい」場合はこの状態で検証します。戻り値まで差し替えたいときは、返ってきたスパイに mockReturnValue や mockImplementation をチェーンします。
const math = { random: Math.random };
const spy = jest.spyOn(math, 'random').mockReturnValue(0.5);
math.random(); // 0.5
expect(spy).toHaveBeenCalled();
mockRestoreによる元実装の復元(spyOn限定の注意)
差し替えたメソッドを元に戻すのが mockRestore() です。ここで実務上よく引っかかる仕様があります。mockRestore が「元の実装を復元」できるのは jest.spyOn で作ったスパイだけで、jest.fn() で作ったモックには元の実装が存在しないため復元されません。jest.fn() を後始末したい場合は mockImplementation を手動で再設定するか、テストの作りを見直します。復元漏れでテスト間の状態が漏れるのを防ぐには、設定ファイルで restoreMocks: true を有効にし、各テストの前に自動復元させるのが確実です。
jest.mock()によるモジュール全体のモック化
個別の関数ではなく、importしているモジュールごと差し替えるのが jest.mock(moduleName) です。HTTPクライアントやDBアクセス層など、テスト対象が import で取り込む依存を丸ごと偽物にできます。
自動モックとファクトリ関数
jest.mock('./api') のように引数がモジュール名だけなら、そのモジュールの関数がすべて自動で jest.fn() に置き換わります(自動モック)。返す中身を自分で定義したいときは、第2引数にファクトリ関数を渡します。
jest.mock('./api', () => ({
fetchUser: jest.fn().mockResolvedValue({ id: 1 }),
}));
ファクトリはファイル先頭へ巻き上げられる(hoisting)ため、外側で宣言した変数を参照するとエラーになります。可変値を差し込みたいときは、テスト本体で import したモックに対して mockResolvedValue などを設定し直す形にします。
jest.requireActualによる部分モック(一部だけ本物の実装)
「モジュールの大半は本物のまま、特定の関数だけ差し替えたい」という部分モックには jest.requireActual(moduleName) を使います。本物の実装を取り込んでスプレッドし、差し替えたい関数だけ上書きします。
jest.mock('./utils', () => ({
...jest.requireActual('./utils'),
now: jest.fn(() => 1700000000000), // 時刻だけ固定
}));
日付・乱数のような一部の非決定的な関数だけ固定し、同じモジュール内の純粋関数はそのまま検証したい、というときに有効です。
jest.requireMockとの違い
対になるのが jest.requireMock(moduleName) で、これは「本物」ではなく「モック版」のモジュールを取得します。jest.requireActual が実装をバイパスして本物を返すのに対し、jest.requireMock は自動モックやファクトリで生成されたモックを明示的に取り出すためのものです。多くの場合はモック済みモジュールを通常の import で受け取れば足りるため、requireMock を直接使う場面は限られます。
mockClear・mockReset・mockRestoreの違い(テスト間のリセット)
テストが増えると「前のテストの呼び出し履歴や差し替えが次に漏れる」問題が必ず出ます。後始末メソッドは3種類あり、どこまで消すかが違います。ここは公式ドキュメントでも混同されやすいと注記されている箇所で、競合記事でも十分に整理されていないため、判断表としてまとめます。
| メソッド | 呼び出し履歴 | 設定した実装 | 元の実装の復元 |
|---|---|---|---|
| mockClear() | 消す | 残す | しない |
| mockReset() | 消す | 消す(undefined返しに) | しない |
| mockRestore() | 消す | 消す | する(spyOn限定) |
使い分けの目安はこうです。呼ばれた回数の検証をテストごとにリセットしたいだけなら mockClear。差し替えた戻り値も含めて初期状態に戻したいなら mockReset。jest.spyOn で包んだ本物を元に戻したいなら mockRestore。個別に呼ぶ代わりに、設定ファイルの clearMocks/resetMocks/restoreMocks を true にすれば各テストの前に全モックへ自動適用され、リセット漏れを構造的に防げます。jest.clearAllMocks() などを beforeEach に置く方法もありますが、設定で一括有効化するほうが書き忘れが起きません。
実践:APIリクエストのモックによる成功・失敗の検証
ここまでのメソッドを組み合わせ、非同期APIを呼ぶ関数のテストを書いてみます。ポイントは、成功と失敗を mockResolvedValueOnce/mockRejectedValueOnce で1回ずつ切り替え、テスト対象のエラーハンドリングまで検証することです。
// api.js を jest.mock で差し替え済みとする
import { fetchUser } from './api';
import { getUserName } from './user';
test('成功時は名前を返す', async () => {
fetchUser.mockResolvedValueOnce({ name: 'Taro' });
await expect(getUserName(1)).resolves.toBe('Taro');
});
test('失敗時は "unknown" にフォールバックする', async () => {
fetchUser.mockRejectedValueOnce(new Error('500'));
await expect(getUserName(1)).resolves.toBe('unknown');
});
実際のネットワークを叩かないため、これらのテストはネットワーク状態に左右されず、ミリ秒単位で完了します。UIコンポーネントを絡めて検証する場合はReact Testing Libraryの使い方と組み合わせ、ブラウザ実描画でコンポーネントを確認するPlaywright Component Testのようなツールと役割を分けると、テスト全体の実行時間を抑えられます。
よくある質問
mockImplementationとmockReturnValueの違いは?
mockReturnValue は決まった値を返すだけの短縮形で、内部的には mockImplementation(() => value) と同じです。mockImplementation は関数そのものを差し込むため、引数による分岐、例外の throw、コールバックの呼び出しといったロジックを再現できます。定数を返すなら mockReturnValue、振る舞いを書きたいなら mockImplementation と使い分けます。
jest.fn()とjest.spyOn()はどちらを使うべき?
新しく独立したモック関数(コールバックや、ファクトリで差し込む関数)が欲しいときは jest.fn()、既存オブジェクトのメソッドを差し替えたい・監視したいときは jest.spyOn() を選びます。jest.spyOn は元の実装を保持し mockRestore() で戻せるため、テスト後に本物へ復元したいケースに向きます。
mockResolvedValueで非同期処理をモックするには?
Promiseを返す関数のモックには mockResolvedValue(値) を使い、テスト側は await か .resolves マッチャーで受けます。失敗を再現するときは mockRejectedValue(new Error(...)) を使い、await expect(fn()).rejects.toThrow(...) で検証します。1回だけ切り替えたいときはそれぞれ ...Once 版を使います。
jest.requireActualとjest.requireMockの違いは?
jest.requireActual はモックを迂回して「本物の実装」を取得します。モジュールの一部だけ本物を残す部分モックで使います。jest.requireMock は逆に「モック版」を明示的に取得するもので、自動モックされたモジュールを取り出す用途に限られます。部分モックで多用するのは requireActual のほうです。
TypeScriptでjest.fnに型を付けるには?
Jest 30系では、対象の関数型を型引数に渡す jest.fn<typeof someFunction>() の書き方で引数・戻り値の型推論が効きます。既にモック化したモジュールを型安全に扱うには jest.mocked(obj) でラップすると、mockReturnValue などが型付きで補完されます。型を明示すると、実装差し替え時の引数ミスをコンパイル時に検出できます。