🙌

Jestで時間をテストしながら開発する

に公開

フロントエンド専門のWeb制作会社「株式会社トゥーアール」の西畑です。

業務でテストコードを書くことは多いのですが特に便利だなと思っているのが時間のテストです。

例えば「一週間以内の記事のみ新着アイコンを表示する」などの要件があった場合に、開発用のAPIにテストデータを入れて一週間に確認するなどはちょっと非効率です。

実際の開発ではAPIの時間は利用せずに確認用の変数を利用することも多いでしょう。

// const { createdAt } = response.json()
// 開発用のデータ、リリース時に戻す
const createdAt = '2025-04-22T10:00:00+09:00'

そういったケースで利用できるのが時間のテストです。

テストしたい関数を作成

今回は指定された時間が1週間以内かどうかを判定する関数を作成してテストしてみましょう。

/**
 * 指定された時間が1週間以内かどうかを判定する関数
 * @param date 判定する日時
 * @returns 1週間以内ならtrue、それ以外はfalse
 */
export function isWithinOneWeek(date: Date): boolean {
  const now = new Date();
  const oneWeekInMs = 7 * 24 * 60 * 60 * 1000; // 1週間のミリ秒数
  const diffInMs = date.getTime() - now.getTime();
  
  return diffInMs >= 0 && diffInMs <= oneWeekInMs;
}

テストコードを作成する1

テストコードには2方向性あって、1つは現在の時刻と調整した時間を比較する方法です。

describe('isWithinOneWeek', () => {
  test('現在時刻は1週間以内と判定される', () => {
    const now = new Date();
    expect(isWithinOneWeek(now)).toBe(true);
  });

  test('1週間後の時刻は1週間以内と判定される', () => {
    const oneWeekLater = new Date();
    oneWeekLater.setDate(oneWeekLater.getDate() + 7);
    expect(isWithinOneWeek(oneWeekLater)).toBe(true);
  });

  test('1週間と1秒後の時刻は1週間以内と判定されない', () => {
    const oneWeekAndOneSecondLater = new Date();
    oneWeekAndOneSecondLater.setDate(oneWeekAndOneSecondLater.getDate() + 7);
    oneWeekAndOneSecondLater.setSeconds(oneWeekAndOneSecondLater.getSeconds() + 1);
    expect(isWithinOneWeek(oneWeekAndOneSecondLater)).toBe(false);
  });

  test('過去の時刻は1週間以内と判定されない', () => {
    const oneDayAgo = new Date();
    oneDayAgo.setDate(oneDayAgo.getDate() - 1);
    expect(isWithinOneWeek(oneDayAgo)).toBe(false);
  });

  test('3日後の時刻は1週間以内と判定される', () => {
    const threeDaysLater = new Date();
    threeDaysLater.setDate(threeDaysLater.getDate() + 3);
    expect(isWithinOneWeek(threeDaysLater)).toBe(true);
  });

  test('8日後の時刻は1週間以内と判定されない', () => {
    const eightDaysLater = new Date();
    eightDaysLater.setDate(eightDaysLater.getDate() + 8);
    expect(isWithinOneWeek(eightDaysLater)).toBe(false);
  });
});

このテストはシンプルですがテストコード自体がロジックを持っているので使いにくいと思っています

テストコードを作成する2

個人的によく利用するのは以下のような時間を固定にしてテストする方法です。

describe('isWithinOneWeek', () => {
  beforeEach(() => {
    jest.useFakeTimers();
    jest.setSystemTime(new Date('2025-04-22T10:00:00+09:00'));
  });
  afterEach(() => {
    jest.useRealTimers();
  });

  test('現在時刻は1週間以内と判定される', () => {
    const time = new Date('2025-04-22T10:00:00+09:00');
    expect(isWithinOneWeek(time)).toBe(true);
  });

  test('1週間後の時刻は1週間以内と判定される', () => {
    const time = new Date('2025-04-29T10:00:00+09:00');
    expect(isWithinOneWeek(time)).toBe(true);
  });

  test('1週間と1秒後の時刻は1週間以内と判定されない', () => {
    const time = new Date('2025-04-29T10:00:01+09:00');
    expect(isWithinOneWeek(time)).toBe(false);
  });

  test('過去の時刻は1週間以内と判定されない', () => {
    const time = new Date('2025-04-22T09:00:00+09:00');
    expect(isWithinOneWeek(time)).toBe(false);
  });

  test('3日後の時刻は1週間以内と判定される', () => {
    const time = new Date('2025-04-25T09:00:00+09:00');
    expect(isWithinOneWeek(time)).toBe(true);
  });

  test('8日後の時刻は1週間以内と判定されない', () => {
    const time = new Date('2025-04-30T09:00:00+09:00');
    expect(isWithinOneWeek(time)).toBe(false);
  });

});

これは jest.useFakeTimers()を利用してDateオブジェクトをモック化してテストを行う方法です。

https://jestjs.io/ja/docs/jest-object#fake-timers

jest.useFakeTimers()ではjest.setSystemTime()で現在の時間を指定できます。

そのためテストコードは現在の時間に足し算引き算をして記述するだけなのでJavaScriptの知識が少なくても様々なテストケースを作成することができます。

テストケースを追加しやすくなるとコードの安心感が増しますので参考にしてください。

株式会社トゥーアール

Discussion