公式サイトのチュートリアルを試します。
JestによるJavaScriptの単体テスト - tanaka's Programming Memoで設定したフォルダーでそのまま作業を続けると良いでしょう。
下準備として、jQueryをインストールしておきます。
npm install --save jquery
公式サイトを意訳
非同期関数を利用するコードのテストをしてみましょう。コードの内容は以下の通りです。
コードは以下の通りです。(公式サイトから転載。練習用のフォルダーにfetchCurrentUser.jsというファイルを作成して以下を入力)
// fetchCurrentUser.js var $ = require('jquery'); function parseUserJson(userJson) { return { loggedIn: true, fullName: userJson.firstName + ' ' + userJson.lastName }; } function fetchCurrentUser(callback) { return $.ajax({ type: 'GET', url: 'http://example.com/currentUser', success: function(userJson) { callback(parseUserJson(userJson)); } }); } module.exports = fetchCurrentUser;
このモジュールのテストコードを書きます。テストコードは__tests__フォルダー内に、fetchCurrentUser-test.jsというファイルを新規に作成して、以下を入力します。
// __tests__/fetchCurrentUser-test.js jest.dontMock('../fetchCurrentUser.js'); describe('fetchCurrentUser', function() { it('calls into $.ajax with the correct params', function() { var $ = require('jquery'); var fetchCurrentUser = require('../fetchCurrentUser'); // Call into the function we want to test function dummyCallback() {} fetchCurrentUser(dummyCallback); // Now make sure that $.ajax was properly called during the previous // 2 lines expect($.ajax).toBeCalledWith({ type: 'GET', url: 'http://example.com/currentUser', success: jasmine.any(Function) }); }); });
fetchCurrentUser.jsがあるフォルダー内で npm test を実行してテストします。
Jestは、ソースフォルダー内にある__tests__フォルダー内からテスト用のJavaScriptを自動的に見つけて実行していきます。
最初のコード(jest.dontMock('../fetchCurrentUser.js');)はとても重要です。これを省略した場合、require()したモジュールは全てモック(テスト用のハリボテ)になります。fetchCurrentUser.jsはテスト対象ですので、本物である必要があります。そこで「dontMock」で、モックにしないように設定しているのです。
このテストは、fetchCurrentUser()が指定のパラメータを渡して$.ajax()を呼び出すかを確認するものです。まずは、fetchCurrentUser()に仮のコールバック関数を渡して呼び出します。$.ajax()はモック関数なので、通信する代わりに渡された引数を記録します。その記録されたオブジェクトがtoBeCalledWith()の引数のオブジェクトの形式かをテストしています。
最初のテストはこれで完了です。しかし、まだテストとしては完全ではありません。$.ajaxの処理が成功した時のコールバックの動作がテストできていません。これをテストするためのコードが以下です(公式サイトから転載。コメントはこちらで追加。describe()のfunction(){}内に追加しましょう)。
// $.ajaxが完了した時にコールバックを呼び出す it('calls the callback when $.ajax requests are finished', function() { var $ = require('jquery'); // これはモック var fetchCurrentUser = require('../fetchCurrentUser'); // これは本物 // Create a mock function for our callback // -- // コールバックのためにモック関数を作成 var callback = jest.genMockFunction(); // 作成したモックのコールバック関数を渡してテスト関数を呼び出す // $.ajax()はモックになっているので通信はされず、$.ajax()に指定した引数が記録される // 後で$.ajax.mock.callsという配列を参照して、記録された値を読み出してテストする fetchCurrentUser(callback); // Now we emulate the process by which `$.ajax` would execute its own // callback // -- // 上で$.ajax()に設定したsuccess関数を、テスト用のパラメータを渡して実行 // このテストコードによって、モック関数callbackの最初の呼び出しの最初の引数に、 // parseUserJson()の結果が記録される $.ajax.mock.calls[0 /*first call*/][0 /*first argument*/].success({ firstName: 'Bobby', lastName: '");DROP TABLE Users;--' }); // And finally we assert that this emulated call by `$.ajax` incurred a // call back into the mock function we provided as a callback // -- // 最後に、モック関数callbackの最初の実行の最初の引数に記録された値が、 // 想定のものかをアサート(確認)して、テスト終了 expect(callback.mock.calls[0/*first call*/][0/*first arg*/]).toEqual({ loggedIn: true, fullName: 'Bobby ");DROP TABLE Users;--' }); });
fetchCurrentUser()は、渡された引数を処理して、結果をcallbackに渡します。fetchCurrentUser()は、内部で$.ajax()を呼び出すので、依存関係があります。Jestはテストで発生する$.ajaxの全てのやり取りが可能なモックを作成します。それを実現するために、本物のモジュールを調査しているので、本物のモジュールも必要です。
Jestでは、全てのモック関数は「.mock」プロパティを持ちます。このプロパティはその関数の全てのやり取りの関数を保持します。今回の例では、中身をmock.callsから読み出しました。この配列は、その関数が何回目に呼ばれたかと、何番目の引数かを添え字にして情報を読み出すことができます。
npm test で、テストが成功することを確認しましょう。
以上で、非同期関数のテストが完了しました。ここで重要なのは、書いたコードが同期的に実行されることです。モック関数を利用する利点です。テスト対象のコードが同期であろうが非同期であろうが、テストコードは常に順次処理で実行できます。
このサンプルの完成コードは examples/tutorial/ にあります。