はじめに
setInterval
を使ったポーリングのテストを書こうとした時に、なかなか動くテストを書けずに詰まりました。
テストしたいコード
今回テストしたかったのは、下のようなコードです。
5分ごとにtestFunction()
が呼ばれるようになっています。
component.js
const Comp = {
// ...
created() {
setInterval(this.testFunction, 300000);
},
// ...
methods: {
testFunction() {
console.log("foo");
}
}
}
動いたテストコード
最終的に動いたのは、jest.spyOn()
でモック関数を作成する方法でした。
component.test.js
import { expect, describe, test } from "@jest/globals";
import { shallowMount } from "@vue/test-utils";
import Comp from "/.../...";
describe("ポーリング", () => {
test("ポーリングのメソッドが5分ごとに呼ばれている", () => {
// testFunction()をspyOnでモックする
const spyTestFunction = jest.spyOn(Comp.methods, "testFunction");
// タイマーモックを有効にする
jest.useFakeTimers();
// マウント
shallowMount(Comp);
// 10分時間を進める
jest.advanceTimersByTime(600000);
// 2回呼ばれているか
expect(spyTestFunction).toHaveBeenCalledTimes(2);
});
});
各コードのはたらき
まず、jest.spyOn()
でモック関数を書いています。
spyOnは、メソッド自体を上書きしているのではなく、テストでもメソッドを呼ぶために名前付けするような働きをしているようです。
const spyTestFunction = jest.spyOn(Comp.methods, "testFunction");
テスト内で時間を自由に操作できるように、フェイクタイマーを有効化します。
jest.useFakeTimers();
ms単位で時間を好きに進めることができます。
jest.advanceTimersByTime(600000);
動かなかったテストコード
動かなかった方もついでに載せておきます。
こちらは、jest.fn ()
を使ってモック関数を作り、マウンティングオプションを使ってメソッドを乗っ取ろうとしましたが、うまくいきませんでした。
component.test.js
import { expect, describe, test } from "@jest/globals";
import { shallowMount } from "@vue/test-utils";
import Comp from "/.../...";
describe("ポーリング", () => {
test("ポーリングのメソッドが5分ごとに呼ばれている", () => {
jest.useFakeTimers();
// モック関数を作成してモックしようとしたけど
const mockTestFunction = jest.fn(() => {});
shallowMount(Comp, {
// どうやらこれではモックが効かないらしい
global: {
mocks: { testFunction: mockTestFunction }
}
}
jest.advanceTimersByTime(600000);
expect(spyAjaxFetchNotification).toHaveBeenCalledTimes(2);
}
}
メソッドはインスタンスではないから、mockとしてかましても動かないのかなと思っています。
終わりに
なんで前者は動いて後者は動いていないのか分かっていません。
jest、奥が深いです。
参考記事