JavaScriptでスパイ、スタブ、モックなどのテストダブルを行う

CodeZine / 2014年5月8日 14時0分

テスト実行結果

 本連載は、テストコードをこれから書こうと考えているJavaScript技術者を対象に、テストコードの意義から、テスト駆動開発、JavaScriptでのテストコードの書き方、継続的インテグレーションなどテスト全般にわたって解説します。また、原理原則だけでなくWhyから説明し、チームメンバーを巻き込みながら開発現場に活かせる考え方を総合的に解説します。第4回目の本稿は、JavaScriptでのテストダブルの方法を、Sinon.jsというJavaScriptテストダブルモジュールを使って説明します。

■対象読者

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)。

[リスト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のユーザ登録処理のテストを行っています。

[リスト1-01]登録処理のテスト(Sinon.js.html)
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(); }); });
[リスト1-02]テスト対象の登録処理(user.js)
var UserService = { register: function(user, callback) { if (!user.valid()){ return; } UserRepository.register(user, callback); } };
 ①のスパイオブジェクトの準備では、UserRepositoryオブジェクトのregister関数メソッドに対してスパイオブジェクトを生成しています。スパイオブジェクトの生成は表1-01のような方法が用意されています。スパイ対象関数の種類によって使い分けることが可能です。

[表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のような機能が用意されています。

[表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, ...); 指定した引数で関数が呼び出された場合にのみ、スパイオブジェクトを返す。

CodeZine

トピックスRSS

ランキング