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 の公式ドキュメントのチュートリアル通りに作成しました。 ↩