レガシーコードのテストをjestに移行していた時に、
jestだとどうやってモックしたり、追跡したりするんだ?となったのでその時のメモ
最初に
相当アホなところでハマったのだが、jest はnamespaceをそのまま読み込むため以下のようの読み込まないといけない。
import'jest'// OKimportjestfrom"jest"// NGjest.xxxx()
特定の関数をモックする
関数にもろもろについてモックの仕方です。
テスト用のモック関数の作成
constmockFunc=jest.fn().mockImplementation(()=>return"mock func");console.log(mockFunc());// mock func
関数自体のモックは jest.fn()に集約されており、単純に何か値を返すのみの関数は mockImplemantionの引数で定義します。
また、mock関数の引数や、どういった値を返したかの検証も行えます。
関数の動作の検証
引数の検証
実際にモックした関数の引数を見たい場合はこんな感じでmock.calls を使います
constmockFunc=jest.fn().mockImplementation((message:string)=>{returnmessage;});mockFunc('test1');mockFunc('test2');console.log(mockFunc.mock.calls);// [ ['test1'], ['test2']]
戻り値の検証
戻り値の場合はmock.resultsを使います。
constmockFunc=jest.fn().mockImplementation((message:string)=>{returnmessage;});mockFunc('test1');mockFunc('test2');console.log(mockFunc.mock.results);/*
[
{
type: 'return',
value: 'test1',
},
{
type: 'return',
value: 'test2',
},
];
*/
value は実際に返した値で、
typeはその値が正常リターンか、エラーかを判定します。
正常の場合は return
エラーがスローされた場合は throw
となります。
また、 imcomplete
というケースがありますが、これはよくわからないので省略します。
後処理
テストの後処理は2種類あり、 関数のモック自体を抹消する場合と、関数の引数などの追跡結果を抹消する場合があります。
constmockFunc=jest.fn().mockImplementation((message:string)=>{returnmessage;});mockFunc.mockClear()// どういった引数を受取、どういった値を返したかの計測結果をリセットmockFunc.mockReset()// 実際に実装した関数や戻り値の設定をリセット
特定のオブジェクトに生えている関数を追跡、モックする
追跡対象を指定
constspy=jest.spyOn(targetObject,"do");// オブジェクトの指定とそこから生えている関数を指定// 関数実行処理expect(spy).toHaveBeenCalled();// 関数が実行されたかどうかを検証
指定した関数をモック関数にする
constspy=jest.spyOn(targetObject,"do").mockImplemention(()=>"spy");
↑のようにすると、指定したオブジェクトの関数は、mockImplementionの中で定義した関数に差し替えられます。
なお、mockImplementionの中には先程解説した jest.fnを渡すこともでき、その場合は引数などの検証も行えると思います(未検証)
オマケ 現在時刻のモック
特定の時刻でポイントが失効するかどうかをチェックしたい場合に現在時刻をモックする必要があったのでその時のことも書いておきます。
重要なことは、日付を扱うパッケージオブジェクト(moment, dayjsなど)はjavascriptのDateオブジェクトに依存しているため、Dateオブジェクトをモックすれば現在時刻系は騙せます。
なので、
- jest で Dateオブジェクトをモックする
- mockDateを使う
- 自分でDateオブジェクトをモックする(レガシーコードはこれをやってた)
個人的に、mockDateを使うのが良いかなと思います。使いやすいしリストアなどもやりやすい他、
ドキュメントも充実しているので、情報共有もしやすいです。
最後に
時刻関連のモックをしらべるのに一番時間がかかりました。
Dateオブジェクトをいじればいいみたいな結果は出てくるのですが、なぜそれが必要なのかという理由が納得できず、
momentの初期化処理のコードを見に行ってDateオブジェクトに依存しているとわかりました。
それでは、良いjestライフを