ダウンロード サンプルファイル (83.4 KB)
本連載は、テストコードをこれから書こうと考えている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)。
<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, ...); | 指定した引数で関数が呼び出された場合にのみ、スパイオブジェクトを返す。 |
この記事は参考になりましたか?
- JavaScriptでテストを書こう!連載記事一覧
- この記事の著者
-
山田 祥寛(ヤマダ ヨシヒロ)
静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
-
WINGSプロジェクト 安西剛(ヤスニシ ツヨシ)
<WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書、記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
この記事は参考になりましたか?
この記事をシェア
