JavaScript でアプリケーション開発をしていく際に、日付をつかさどる Date
オブジェクトを使うことがちょくちょくあると思います。1
Date.now
のようなメソッドをモック化することはとても簡単で、単に jest.fn()
で置き換えてしまえばよいです。
Date.now = jest.fn(() => 1482363367071);
公式ドキュメントにもやり方が記載されています↓↓↓
2. Tests should be deterministic
しかし、Date
は実行時によって値が変わってしまいますので、ちょっとテストではそのまま使うのは難しく、できれば Date
をモック化して同じ時間を返してくれると色々都合が良いです。
サンプルコード2
まずはテスト対象ファイルのコードです。
const getTimestamp = () => new Date().getTime()
module.exports = getTimestamp
シンプルに Date
オブジェクトのインスタンスを生成し、getTime()
メソッドを実行してメソッド実行時のミリ秒を取得しています。そもそもこのメソッドならテストする必要なくない?という議論はあると思いますが、サンプルですのでご了承いただけると…
次に、こちらのファイルをテストするテストコードです。
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 の紹介)
-
今回のデモの環境については割愛します。Jest の公式ドキュメントのチュートリアル通りに作成しました。 ↩