throttleのテストってどうやって書けばいいんだ...ってなったのでまとめます。
このテストで全部網羅できているわけではないと思うので、
「この項目もテストする必要があるよね!」等あればご教授くださいm
前提
jestを使用したテストです。
今回はタイマーモックを使用します。詳しくは公式をご覧ください
テスト対象のコード
const throttle = (fn: () => void, interval: number): (() => void) => {
let time = Date.now() - interval;
return () => {
if (time + interval < Date.now()) {
time = Date.now();
fn();
}
};
};
テストする項目
今回テストした項目は以下です。
共通処理
タイマーモックを使うので、beforeEachでuseFakeTimers
をセットして
afterEachでuseRealTimers
でリセットします。
describe('throttle', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
})
throttle関数の引数のfn()が呼ばれないパターン
import { throttle } from '置かれてる場所'
it('fn()呼ばれないパターン', () => {
const fn = jest.fn();
const throttleFn = throttle(fn, 1000);
throttleFn();
expect(fn).not.toBeCalled();
});
一見呼ばれてそうだな思いましたが、タイマーを進めていないので呼ばれていないです。
一定時間経過後に何回fn()が呼ばれるかパターン
it('3回呼ばれるパターン', () => {
const fn = jest.fn();
const throttleFn = throttle(fn, 1000);
throttleFn();
expect(fn).not.toBeCalled();
//ここまでは上のテストと同様です。
for (let i = 0; i < 10; i++) {
throttleFn();
jest.advanceTimersByTime(500);
}
expect(fn).toBeCalled();
expect(fn).toHaveBeenCalledTimes(3);
});
advanceTimersByTime
は指定した時間タイマーを進めることができます。
今回は引数に500を取っているので、500ms分タイマーが進みます。
throttle関数は1000ms経過後にfn()を呼び出すようにする関数です。
つまり1000ms経過しない限り呼ばれません。
for (let i = 0; i < 10; i++) {
throttleFn();
jest.advanceTimersByTime(500);
}
この部分をもっと噛み砕くと下記のような順番で処理が実行されます。
i=0
throttleFn();
jest.advanceTimersByTime(500) //500msタイマーを進める
i=1
throttleFn();
jest.advanceTimersByTime(500) //500msタイマーを進める
ここの段階で1000ms時間経過しています。まだfn()は呼ばれません
i=2
throttleFn();
jest.advanceTimersByTime(500) //500msタイマーを進める
1500ms時間経過しているので、この段階でやっとfn()がよばれます。
メモ
呼ばれたかどうかのチェックと余裕があれば回数チェックもできるといいかなと思いました。
参考記事