対象読者
- JavaScriptの基本をある程度理解している方
- テストコードをこれから書こうと考えている方
テストダブルとは
テストコードを実行する際に、次のような課題に直面したことはないでしょうか。
- Ajaxへのサーバへの問い合わせのような外部リソースも含めた処理のテストを行う必要があるが、テストデータやサーバなど簡単に用意できない
- 多くの処理を行っている関数をテストする場合、その関数から正しく別の関数が呼ばれているか確認できない
こういった課題を解決するのがテストダブルです。Ajaxなどの外部リソースや扱いにくいオブジェクトを、代役となるオブジェクトに置き換えてテストを行うことができます。置き換えたオブジェクトや関数に対して戻り値を指定したり、該当関数が何回呼ばれたかなどを確認することも可能です。テストダブルのパターンとしては、主に以下の4つの方法があります。
-
スパイ
関数がどのように呼び出されたかを記録する -
スタブ
関数の戻り値をあらかじめ設定し、その結果でテストを行う -
モック
実行前に関数の実行回数など期待する結果を指定しておく -
フェイク
問い合わせるDBやサーバ処理などを単純な実装に置き換える
JavaScriptでは、Sinon.js、Jasmineなどのライブラリがテストダブルの機能を持っています。本稿では、Sinon.jsを使ったテストダブルのパターンを説明します。
Sinon.jsとは
Sinon.jsとは、JavaScriptでテストダブルを実現するライブラリです。スパイ、スタブ、モック、フェイクの代表的なテストダブルの機能を持っています。Sinonは、ギリシヤ神話の「トロイ戦争」でスパイとして出てくる人物です。
使用方法は、公式サイトからsinon-1.9.0.jsをダウンロードし読み込みます。フェイク機能を使用する場合は、ieの場合にsinon-ie-1.9.0.jsも読み込む必要があります(リスト01)。
<script type="text/javascript" src="js/sinon-1.9.0.js"></script> <!--[if IE]> <script type="text/javascript" src="js/sinon-ie-1.9.0.js"></script> <![endif]-->
ここからは、Sinon.jsが持っている4つのパターンの機能を1つずつ紹介します。なお、ここでのサンプルは、テスティングフレームワークはmocha、アサーションモジュールはchai.js使用しています。mochaとchaiについては、本連載の第2回「Mochaを使ってJavaScriptのテストをブラウザで実行してみよう」を参照してください。また、ここで紹介する以外にも多くの関数が用意されています。それらの関数は公式ドキュメントを参照してください。
スパイ
スパイは、テスト対象の関数に対して、実行時の引数や戻り値、発生した例外を記録する機能を持ちます。メソッドに引数に渡すための無名関数にしたり、既存の関数に対してラップすることもできます。
早速実際のコードを見ていきましょう。リスト1-01は、リスト1-02のユーザ登録処理のテストを行っています。
describe("登録処理のテスト", function() { it("登録処理が行われたか", function() { /** 事前準備 */ var userName = new UserName('Koike Naoki'); var mailAddress = new MailAddress('koike@koikenaoki.com'); var user = new User(userName, mailAddress); var callback = function() {}; // スパイオブジェクトの準備 ① var spy = sinon.spy(UserRepository, "register"); /** 実行 */ UserService.register(user, callback); /** 確認 ② */ expect(spy.withArgs(user, callback).calledOnce).to.be.true; /** 事後処理 */ // スパイオブジェクトをクリア UserRepository.register.restore(); }); });
var UserService = { register: function(user, callback) { if (!user.valid()){ return; } UserRepository.register(user, callback); } };
①のスパイオブジェクトの準備では、UserRepositoryオブジェクトのregister関数メソッドに対してスパイオブジェクトを生成しています。スパイオブジェクトの生成は表1-01のような方法が用意されています。スパイ対象関数の種類によって使い分けることが可能です。
呼び出し方法 | 概要 |
---|---|
var spy = sinon.spy(); |
空のスパイオブジェクトを生成。 主にコールバックで渡す無名関数などに対して使用する。 |
var spy = sinon.spy(myFunc); | 特定の関数に対してスパイオブジェクトを作成する。 |
var spy = sinon.spy(object, "method"); | オブジェクト内のメソッドに対してスパイオブジェクトを生成する。 |
その後、テスト対象の処理を実行し、リスト1-01の②では、引数が「user」と「callback」で、1回だけ呼ばれたかをcalledOnceを指定することにより確認しています。このように、対象の関数に対して呼び出された際の情報が記録され、想定通りに実行されたかを確認することができます。なお、スパイは呼び出された情報を記録するだけでなく、スパイ対象の関数の処理も実行されます。他に関数に対して行われた処理を確認する方法は、主に表1-02のような機能が用意されています。
呼び出し方法 | 概要 |
---|---|
spy.calledWith(arg1, arg2, ...); | 指定した引数で関数が呼び出されたかを確認する。 |
spy.callCount | 呼び出された回数を返す。 |
spy.called | 呼び出された場合にtrueを返す。 |
spy.calledOn(obj); | 指定したオブジェクトで関数が実行された場合trueを返す。 |
spy.calledOnce | 1回呼び出された場合trueを返す。 |
spy.calledTwice | 2回呼び出された場合trueを返す。 |
spy.exceptions | 発生したexceptionを返す。 |
spy.returnValues | 実行後の戻り値を返す。 |
spy.withArgs(arg1, arg2, ...); | 指定した引数で関数が呼び出された場合にのみ、スパイオブジェクトを返す。 |