はじめに
こんにちは、Webエンジニアの柴田です。
今回はJestでwindowをモックする方法をご紹介します。
windowをモックすることで、windowを使ったスクロールのテストやwindowのリサイズのテストなどが可能になります。
環境
Jest: 27.5.1
TypeScript: 4.6.4
実装
jest.spyOnについて
元の関数の実装をモック関数で上書きするだけでなく、元の実装を残したまま、対象の関数を監視することができる関数です。
jest.spyOnを使ってwindowをモック
まず、windowをモックする変数を定義します。
モック方法はJest
のspyOn
関数を使います。
// 第三引数が'get'の理由は、windowオブジェクトのgetメソッドをモックしたいため
const windowSpy = jest.spyOn(window, 'window', 'get');
今回はなぜjest.spyOn
を採用したのか?
元の関数の実装を追跡して、元の関数で呼ばれたどうかを含めたテストを想定して実装するためです。
またmockImplementation
関数を使い、windowSpyにモック処理を追加することもできます。 mockImplementation
関数は、元の実装をモックで上書き実装する関数です。
例えば、windowのスクロールやリサイズのモック処理を追加してみます。
windowSpy.mockImplementation(() => ({
scrollTo: () => jest.fn(),
ResizeObserver: jest.fn().mockImplementation(() => ({
disconnect: jest.fn(),
observe: jest.fn(),
}))
}) as unknown as Window & typeof globalThis);
実際にテストを書いて、windowをモックできたかを確認しましょう。
スクロールできたかどうかを確認するテストを書きます。
describe('windowのモック テスト', () => {
test('windowのscrollTo関数が呼ばれているかどうかテスト', () => {
// モックをセット
const windowSpy = jest.spyOn(window, 'window', 'get');
windowSpy.mockImplementation(() => ({
scrollTo: () => jest.fn(),
ResizeObserver: jest.fn().mockImplementation(() => ({
disconnect: jest.fn(),
observe: jest.fn(),
}))
}) as unknown as Window & typeof globalThis);
// スクロール
window.scrollTo();
// 呼ばれたかどうかCheck
expect(windowSpy).toHaveBeenCalled();
// 呼ばれた回数もCheck
expect(windowSpy).toHaveBeenCalledTimes(1);
// リセット処理
windowSpy.mockRestore();
});
});
実行結果
無事テストが通りました。
補足
jest.fn
とjest.spyOn
の違いと使い分け
・違いjest.fn
元の関数の実装をモック関数で上書きする関数jest.spyOn
元の関数の実装をモック関数で上書きするだけでなく、元の実装を残したまま、対象の関数を監視することができる関数
・使い分けjest.fn
を使う場合、テスト時に関数の元の実装を意識しなくて良い場合jest.spyOn
を使う場合、元の実装を残したまま監視したい場合やテストごとにモックした関数の実装を元に戻したい場合
注意点
モックのクリーンアップの記述を忘れずに書きましょう。
他のテストの結果に影響を与えないためです。
mockRestore()
関数を使い、モックを元の実装に戻します。
・mockRestore()
jest.spyOn()
によって作成したモックを元の値に戻す関数になります。
windowSpy.mockRestore();
まとめ
以上、JestでspyOn関数を使って、windowをモックする方法をご紹介しました。
今回記事で紹介したコードのフルバージョン
describe('windowのモック テスト', () => {
test('windowのscrollTo関数が呼ばれているかどうかテスト', () => {
// モックをセット
const windowSpy = jest.spyOn(window, 'window', 'get');
windowSpy.mockImplementation(() => ({
scrollTo: () => jest.fn(),
ResizeObserver: jest.fn().mockImplementation(() => ({
disconnect: jest.fn(),
observe: jest.fn(),
}))
}) as unknown as Window & typeof globalThis);
// スクロール
window.scrollTo();
// 呼ばれたかどうかCheck
expect(windowSpy).toHaveBeenCalled();
// 呼ばれた回数もCheck
expect(windowSpy).toHaveBeenCalledTimes(1);
// リセット処理
windowSpy.mockRestore();
});
});