Jest で new Date() をモック化する


JavaScript でアプリケーション開発をしていく際に、日付をつかさどる Date オブジェクトを使うことがちょくちょくあると思います。1

Date.now のようなメソッドをモック化することはとても簡単で、単に jest.fn() で置き換えてしまえばよいです。

Date.now = jest.fn(() => 1482363367071);

公式ドキュメントにもやり方が記載されています↓↓↓
2. Tests should be deterministic

しかし、Date は実行時によって値が変わってしまいますので、ちょっとテストではそのまま使うのは難しく、できれば Date をモック化して同じ時間を返してくれると色々都合が良いです。

サンプルコード2

まずはテスト対象ファイルのコードです。

datetime.js
const getTimestamp = () => new Date().getTime()
module.exports = getTimestamp

シンプルに Date オブジェクトのインスタンスを生成し、getTime() メソッドを実行してメソッド実行時のミリ秒を取得しています。そもそもこのメソッドならテストする必要なくない?という議論はあると思いますが、サンプルですのでご了承いただけると…

次に、こちらのファイルをテストするテストコードです。

datetime.test.js
import getTimestamp from './datetime'

const mockDate = new Date(1594374371110)
const spy = jest.spyOn(global, "Date")
                .mockImplementation(() => mockDate)
test('get timestamp', () => {
  const actual = getTimestamp()
  const expected = 1594374371110

  // ここはなくても良い
  expect(spy).toHaveBeenCalled()
  expect(actual).toBe(expected)

  // 初期化も忘れずに
  spy.mockReset()
  spy.mockRestore()
})

解説

一言で言ってしまうと、spyOn() メソッドを使った だけです。公式ドキュメントの spyOn() メソッドの説明を見た時「まさにこれやん」と思いました。

本記事の肝はこの3行ですね。説明のため、一部改行しています。

// ①
const mockDate = new Date(1594374371110)
const spy =
  // ②
  jest.spyOn(global, "Date")
      // ③
      .mockImplementation(() => mockDate)

①で Date オブジェクトのインスタンスを、モック用の固定した日付とセットで生成します。

②で spyOn メソッドを使い、グローバルオブジェクトの Date() メソッドに spyOn() メソッドでスパイするように設定しています。

③が最も重要な処理です。ここではスパイした後 mockImplementation() メソッドでレスポンスを指定しています。この処理がないと、new Date() を実行した場合のレスポンスは mockConstructor というモックオブジェクトになってしまい、getTime() メソッドが存在しない、というエラーが発生してしまいます。

ちなみにレスポンスを spy という変数に格納していますが、ここはなくても良いですが、私は spy 系のメソッドを使うならちゃんとコールされているよね?と言うことも確認したいため、受け取っています。

この処理により、datetime.js の getTimestamp() メソッド内で行っている new Date() は必ず同じ時間でインスタンスが生成されるようになります。誰かの参考になれば。

ではでは(=゚ω゚)ノ

P.S.

ちなみに以下の記事にもあるように jest-date-mock というモジュールを使うという Ultimate Solution もありますので、参考までに添付します。

JS の Date をテスト時にモックしたい(jest-date-mock の紹介)


  1. もちろん momentdayjs を使う場合は本記事は関係ないです。 

  2. 今回のデモの環境については割愛します。Jest の公式ドキュメントのチュートリアル通りに作成しました。