書籍転載:JavaScriptライブラリ実践活用[厳選111]

書籍転載:JavaScriptライブラリ実践活用[厳選111]

JasmineのSpy機能でテストダブルを作成する

2013年6月13日

書籍転載の12本目(書籍内の番号は「109」)。Jasmineでテスト対象オブジェクトが持つメソッドの戻り値を固定値に変更したり、そのメソッドが実行されたかどうかを検証したりするために、Spy機能を使用する方法を解説。

WINGSプロジェクト 高野 将
  • このエントリーをはてなブックマークに追加

書籍転載について

 本コーナーは、技術評論社発行の書籍『JavaScriptライブラリ実践活用[厳選111]』の中から、特にBuild Insiderの読者に有用だと考えられる項目を編集部が選び、同社の許可を得て転載したものです。

 『JavaScriptライブラリ実践活用[厳選111]』の詳細や購入は技術評論社のサイト目次ページをご覧ください。

ご注意

本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。

Jasmineでテスト対象オブジェクトが持つメソッドの戻り値を固定値に変更したり、そのメソッドが実行されたかどうかを検証したりするために、Spy(スパイ)を使用します。

JasmineのSpy(スパイ)機能

 Webサービス呼び出しなど外部の処理を呼び出している箇所をテストするため、「テストダブル(テスト代役)」を使って処理を差し替えるというのはよく行われます。テストダブルとは、テストのための特定の箇所の処理を置き換えた、元のオブジェクトの代役となるオブジェクトのことです。

 Jasmineでテストダブルを作るには、Spy(スパイ)という機能を使います。Spyを使うとメソッド単位で処理を置き換えたり、期待したとおりに呼び出されたかどうかを検証することができます。

Spy機能の使用方法

 リスト109-01のようなGreeterクラスをテストすることを考えてみましょう。このGreeterクラスでは、maikeGreetingメソッドの中で現在の時刻を使用しているため、何もしなければテストを行った時間によって、テスト結果が変わってしまいます。これではテストの意味がありません。

JavaScript
var Greeter = (function () {
  function Greeter() { }

  // 現在の時間を返す
  Greeter.prototype.getNowHours = function () {
    return new Date().getHours();
  };

  // 名前と時間からあいさつ文を作る
  Greeter.prototype.makeGreeting = function (hours, name) {
    var greeting;
    if (4 < hours && hours < 12) {
      greeting = "Good morning";
    } else if (12 <= hours && hours < 18) {
      greeting = "Hello";
    } else {
      greeting = "Good evening";
    }
    return greeting + ", " + name;
  };

  // 名前と現在の時間からあいさつを返す
  Greeter.prototype.greet = function (name) {
    var hours = this.getNowHours();
    return this.makeGreeting(hours, name);
  };

  return Greeter;
})();
リスト109-01 Spy対象クラス(Greeter.js)

 Spy機能を使うと、現在の時刻を取得するgetNowHoursメソッドの処理を、自分の指定した動作に置き換えることができます。その他、対象とするオブジェクトのメソッドがただ駆使呼び出されたかどうかを検証することもできます。

 Spy機能を使用したテストコードをリスト109-02、動作イメージを図109-01に示します。順に説明していきましょう。

JavaScript
describe("Spyを使用したテスト", function () {
  var greeter;

  beforeEach(function () {
    greeter = new Greeter();
  });

  // 1Spyオブジェクトを作成する
  it("Spyオブジェクトを作成する", function () {
    var spyObject = spyOn(greeter, "makeGreeting");

    expect(spyObject).toBe(greeter.makeGreeting);
    expect(greeter.makeGreeting(5, "Sho")).toEqual(undefined);
  });

  // 2元の処理を行う
  it("元の処理を行う", function () {
    spyOn(greeter, "makeGreeting").andCallThrough();

    expect(greeter.makeGreeting(5, "Sho")).toEqual("Good morning, Sho");
  });

  // 3任意の戻り値を返す
  it("任意の戻り値を返す", function () {
    spyOn(greeter, "getNowHours").andReturn(5);

    expect(greeter.getNowHours()).toEqual(5);
  });

  // 4任意の処理を行う
  it("任意の処理を行う", function () {
    var called = false;
    spyOn(greeter, "makeGreeting").andCallFake(function (hour, name) {
      called = true;
      expect(hour).toEqual(5);
      expect(name).toEqual("Sho");
      return "Greeting";
    });

    expect(greeter.makeGreeting(5, "Sho")).toEqual("Greeting");
    expect(called).toEqual(true);
  });

  // 5例外を発生させる
  it("例外を発生させる", function () {
    spyOn(greeter, "getNowHours").andThrow("例外");

    expect(greeter.getNowHours).toThrow("例外");
  });

  // 6メソッド呼び出しを検証する
  it("メソッド呼び出しを検証する", function () {
    spyOn(greeter, "getNowHours").andReturn(5);
    spyOn(greeter, "makeGreeting").andCallThrough();

    expect(greeter.greet("Sho")).toEqual("Good morning, Sho");

    // 引数なしのメソッド呼び出しを検証する
    expect(greeter.getNowHours).toHaveBeenCalled();
    // 引数ありのメソッド呼び出しを検証する
    expect(greeter.makeGreeting).toHaveBeenCalledWith(5, "Sho");
    // 任意の引数でのメソッド呼び出しを検証する
    expect(greeter.makeGreeting).toHaveBeenCalledWith(jasmine.any(Number), jasmine.any(String));
  });
});
リスト109-02 Spy使用テストコード(GreeterSpec.js)
図109-01
図109-01

1Spyオブジェクトを作成する

 Spyオブジェクトを作成するには、spyOnメソッドを使います。spyOnメソッドの引数には、対象オブジェクトとSpy化するメソッドを指定します。すると、引数で指定したメソッドがSpyオブジェクトになり、戻り値として戻されます。

 Spyオブジェクトを作成した段階では、このメソッドを呼び出しても結果はundefinedになります。

2元の処理を行う

 Spyオブジェクトに元の処理を行わせるには、andCallTroughメソッドを使います。サンプルでは1とは違い、makeGreetingメソッドの元の処理が実行され、期待したとおり"Good morning, Sho"が返されます。

3任意の戻り値を返す

 任意の戻り値を返すようにするには、andReturnメソッドを使います。引数には戻したい値を指定します。サンプルではgetNowHoursメソッドが現在の時間に関係なく、5を返すようになります。図109-01のイメージは、このandReturnメソッドの動作になります。

4任意の処理を行う

 任意の処理を行わせるには、andCallFakeメソッドを使います。引数には行わせたい処理を行うコールバック関数を指定します。コールバック関数の引数はSpy化対象のメソッドと同じになります。サンプルでは、コールバック関数の外で定義した値への代入や、引数の検証を行っています。

 andCallFakeを利用すると、複数のフィールドを持つオブジェクトを引数として受け取るようなメソッドで、それぞれのフィールの値を検証することができます。

5例外を発生させる

 例外を発生させるには、andThrowメソッドを使います。引数にはスローさせる例外オブジェクトを指定します。リスト109-02では“例外”という文字列を例外オブジェクトとして使い、例外を発生させています。

6メソッド呼び出しを検証する

 メソッド呼び出しが正しく行われたかどうかを検証するには、引数なしのメソッドはtoHaveBeenCalledメソッド、引数ありのメソッドはtoHaveBeenCalledWithメソッドを使います。それぞれ、expectメソッドの引数に検証対象のSpyオブジェクトを指定して、呼び出します。

 toHaveBeenCalledWithメソッドには、引数の値まで指定して検証することが可能です。ただ、どのような値でもとりあえず「呼び出された」ということだけ検証したい場合もありますので、その時はjasmine.anyメソッドを使うと便利です。jasmine.anyメソッドは引数に型を指定します。

※以下では、本稿の前後を合わせて5回分(第10回~第14回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

10. [Underscore.js]テンプレートとオブジェクトから文字列を生成する

書籍転載の10本目(書籍内の番号は「101」)。テンプレートとオブジェクトをバインドし、その結果を出力できる「Underscore.js」のテンプレートAPIの使い方を説明。

11. RSpec風の構文でBDD用のテスト・コードを記述する[Jasmine]

書籍転載の11本目(書籍内の番号は「108」)。RSpec風のテスト・コードが書けるBDD(ビヘイビア駆動開発)テスティング・フレームワーク「Jasmine」の基本的な使い方を説明する。

12. 【現在、表示中】≫ JasmineのSpy機能でテストダブルを作成する

書籍転載の12本目(書籍内の番号は「109」)。Jasmineでテスト対象オブジェクトが持つメソッドの戻り値を固定値に変更したり、そのメソッドが実行されたかどうかを検証したりするために、Spy機能を使用する方法を解説。

13. 短くかつ安全で高性能なコードを書く[CoffeeScript]

書籍転載の13本目(書籍内の番号は「62」)。短い記述で、安全かつ高性能なJavaScriptコードを生成できる「CoffeeScript」の基本的な使い方を説明する。

14. オブジェクト指向でコードを記述する[CoffeeScript]

書籍転載の14本目(書籍内の番号は「63」)。CoffeeScriptを使用すると、クラスベースのオブジェクト指向で記述できる。その内容を説明する。

イベント情報(メディアスポンサーです)

Twitterでつぶやこう!


Build Insider賛同企業・団体

Build Insiderは、以下の企業・団体の支援を受けて活動しています(募集概要)。

ゴールドレベル

  • 日本マイクロソフト株式会社
  • グレープシティ株式会社