目次

中堅SE向け研修

テストの続き

Node.jsとJasmineとKarmaでのモジュールテストの続き。

ここでは、複数のモジュールが連携している場合のテストと、モックの利用について説明する。

依存性の高いモジュールのテスト

3つのモジュール、Product,Wallet,Bankがあり、それぞれ以下のような役割分担を持つ。

下に行くほど、上のモジュールに依存している。

モジュールのソース

テスト

単体テストを行う場合、対象となるモジュールが期待通りに動いていることを確認することが必要である。

例えば、Bankモジュールをテストする場合、Bankが提供する機能のうち、他のモジュールが行っていることではない機能についてテストする。他のモジュールの機能はそのモジュールの単体テストによってテストされるべきである。

モックを使用したテスト

モックとは「偽物」や「模造品」のことで、本物のふりをして本物らしく動作するけれども本物ではないものを意味する。

ちなみにモックの他に似たような言葉としてスタブ、フェイク、ダミーなどがあり、正確に言うとそれぞれで意味が異なるのだが、その辺の細かいことはここでは省略。

ここでは、テストスペックにモックを使用するためにrewireというモジュールを使用する。rewireモジュールはすでに前回のセミナーで、Node.jsのnpmによってインストール済み。(第4回参照)

モックを使用する状況

どのような状況でモックが使用されるのか?

例えば、上記の3つのプログラムを3名の開発者が開発しており、まだProductとWalletは出来上がっていないが、Bankだけはもう完成してテストしたい、というような状況である。

この場合、ProductとWalletをモックにしてBankSpecに注入することにより、まだ実体のないProductとWalletに依存するBankをテスト可能となる。

モックの注入方法

rewireを使ったモックの注入手順は以下の通り。

  1. rewireをrequireする
  2. テストモジュールをrequireではなくrewire(Module)で取り込む
  3. 必要なモジュールのインターフェイスだけを持つモックを作成する
  4. Module.__set__('差し替えたいモジュール名', 差し替えたいモジュール)を実行する

モックを利用したBankのテスト

下記のようにモックを注入し、src/Product.jsとsrc/Wallet.jsの中身をすべてコメントアウトしてから、テストする。

var rewire = require("rewire");
var Bank = rewire("../src/Bank");
//var Wallet = require("../src/Wallet");
//var Product = require("../src/Product");
// 製品モック
var Product = function (name, price) {
  this.name = name;
  this.price = price;
  this.print = function () {};
};
// 財布モック
var Wallet = function (name, amo) {
  this.name = name;
  this.amo = amo;
  this.print = function () {};
  this.buy = function (p) {};
  this.charge = function (a) {};
};
// モックの注入
Bank.__set__('Product',Product);
Bank.__set__('Wallet',Wallet);
// 実際のテストを行う
describe("銀行のテスト", function () {
  var bank, taro, hanako;
  describe("生成テスト", function () {
    it("最初の財布の数は0だ", function () {
      bank = new Bank();
      expect(bank.wallets.length).toBe(0);
    });
  });
  describe("預入のテスト", function () {
    beforeEach(function () {
      bank = new Bank();
      taro = new Wallet("太郎", 10000);
      hanako = new Wallet("花子", 25000);
    });
    it("太郎と花子の財布を預けた", function () {
      bank.keep(taro);
      bank.keep(hanako);
      expect(bank.wallets.length).toBe(2);
    });
    it("太郎と太郎の財布を預けた", function () {
      bank.keep(taro);
      bank.keep(taro);
      expect(bank.wallets.length).toBe(1);
    });
  })
  describe("返却のテスト", function () {
    // beforeEachをしないと、直上の状態が引き継がれる
    it("太郎の財布を返した", function () {
      bank.keep(hanako);
      var ret_wallet = bank.back('太郎');
      // 花子の財布だけが残っているはず
      expect(bank.wallets.length).toBe(1);
      expect(bank.wallets[0].name).toBe("花子");
      // 返された財布は太郎のもののはず
      expect(ret_wallet.name).toBe("太郎");
    });
  });
  describe("チャージのテスト", function () {
    it("太郎に5000円チャージした", function () {
      taro = new Wallet("太郎", 10000);
      spyOn(taro, 'charge');
      bank.keep(taro);
      bank.charge("太郎", 5000);
      expect(taro.charge).toHaveBeenCalled();
    });
  });
  describe("引落しのテスト", function () {
    it("太郎がカバンを買った", function () {
      var bag = new Product("カバン", 5000);
      spyOn(taro, 'buy');
      bank.payForProduct("太郎", bag);
      expect(taro.buy).toHaveBeenCalled();
    });
  });
  describe("新しい財布の生成テスト", function () {
    it("3000円入りの次郎の財布を作った", function () {
      var w = bank.newWallet("次郎", 3000);
      expect(w instanceof Wallet).toBeTruthy();
    })
  });
});

KarmaによるブラウザUIテスト

Karmaとはブラウザベースのテスト環境で、通常はユーザが手作業で行わなければならない、テキスト欄に文字を入れたりボタンをクリックしたりして行うテストを自動化できる。

Karmaは様々なブラウザに対応しているが、ここではChromeだけを対象として検証する。

なお、Karmaと並んで有名なUIテスト環境としてSeleniumがある。

Karmaのインストールも前回に済ませてある。

karma.conf.jsの編集

前回のインストール時に、

>karma init

を実行してプロジェクトルートフォルダにkarma.conf.jsというファイルを作ってあるが、それに次の設定を追加する。

module.exports = function (config) {
  config.set({
    …
    files: [
      '*.html',       // ←ここ
      'src/*.js',
      'spec/*Spec.js' // ←ここも
    ],
    …
    preprocessors: {
      '*.html': 'html2js'   // ←ここ
    },
    …
  });
};

最初の設定はテスト対象として.htmlファイルも含むようにする。

2つ目の設定は.htmlファイルを読むときhtml2jsという前処理を行うようにする。karma-html2js-preprocessorというモジュールも前回インストール済みである。

ソースコードの作成

ブラウザテストを行うには、通常のウェブアプリと同様にHTMLファイルをJavascriptファイルが必要となる。

テストコードの作成

Karmaでテストを行う場合は、テストファイルの中にrequire()を書く必要はない。karma.conf.jsの中の['src/*.js','spec/*.js']で相互参照が行われるからである。

また、テストコードの最初で、html2jsを使用してHTMLをJavascriptとして扱えるようにしている。これにより、DOMツリーが使用できる。

テストの実行

Karmaを使ったテストは以下のように実行する。

karma start karma.conf.js

これで、設定ファイルに従って自動的にブラウザが起動し、テストが実行される。

パスが通っていない場合、以下のようにしてパスを通す。

>set Path=.\node_modules\.bin;%Path%

Karmaレポータの指定

karma-html-reporterを使うと、テストの結果を見やすく表示してくれる。

そのためには、karma.conf.jsを以下のように修正する。

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    //reporters: ['progress'],     // ←コメントアウト
    reporters: ['spec','html'],    // ←追加

ディレクティブ

ディレクティブとは、HTML命令を拡張する機能である。

AngularJSには、最初から多くのディレクティブが準備されており、それを組み合わせて使うことでほとんどの処理は実現できる。

したがって、あまり独自のディレクティブを作らなければならないような場面は少ないと思うが、ここでは例としてディレクティブの作成について説明する。

AngularJSのディレクティブAPI

独自のディレクティブは以下のような構文で宣言する。

var app=angular.module("App");
app.directive("DirectiveName",function(){
  return {
    // ここに、必要なディレクティブの設定項目を書く
  };
});

設定項目には以下のものがある。

項目名意味
restrictこのディレクティブの使われ方。要素(E)、属性(A)、クラス(C)、およびコメント(M)を組み合わせて文字列で指定する。デフォルトは'EA'である。
priority同じ要素で指定されているディレクティブの優先順位を決める数値。デフォルトは0。
templateインラインのテンプレート文字列。この文字列がHTMLとして使用される。
templateUrlテンプレートとして使う文字列が入っているURL。長い場合はこちらが良い。
replacetrueならばもとのHTMLがディレクティブのHTMLで置き換わる。falseならば挿入される。デフォルトはfalse。
transcludetrueの場合、元のHTMLの子要素がディレクティブのHTMLのng-transcludeで指定されている要素へと移される。
scope親の$scopeを継承せずに、このディレクティブのために新しい$scopeを生成する。
controllerディレクティブ間での通信を可能にするコントローラを生成する。
requireこのディレクティブが正しく機能するために必要な他のディレクティブを指定する。
linkディレクティブが生成された後で、DOMを変更したりイベントリスナーを追加したりデータバインディングを定義したりする。
compileディレクティブが生成される際に1回だけ呼び出される処理を記述する。$scopeは利用できない。link関数を返すこともできる。

ディレクティブの例1

ディレクティブの例2

ディレクティブの例3

開発実習

AngularJSを使ったウェブアプリの開発の課題である。

数字が大きくなるにつれて複雑になるので、まずは1から順にこなしていってほしい。

課題1

課題2


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS