2011年12月19日

Node.jsのテスティングフレームワーク「Mocha」(前編)

Mochaのチュートリアルに沿ってMocha(読み:もか)を使ってみました。

長いので前編・後編の二部構成にします。

使用するMochaのバージョンは0.3.6です。



はじめに

このエントリーのために書いたサンプルコードは以下に置いておきます。

https://github.com/ryu22e/mocha-example

Features

Google翻訳と辞書を頼りに翻訳しました。もっと適切な訳があればご指摘お願いします。

  • browser support(ブラウザサポート)
  • simple async support(シンプルな非同期サポート)
  • proper exit status for CI support etc(CIサポート等のための適切な終了ステータス)
  • auto-detects and disables coloring for non-ttys(非ttysのための自動検出とカラーリング無効)
  • maps uncaught exceptions to the correct test case(補足されない例外を正しいテストケースにマッピング)
  • async test timeout support(非同期テストのタイムアウトのサポート)
  • test-specific timeouts(特定のテストのタイムアウト)
  • growl notification support(Growlサポート)
  • reports test durations(テスト期間のレポート)
  • highlights slow tests(遅いテストのハイライト表示)
  • file watcher support(ファイル監視のサポート)
  • global variable leak detection(グローバル変数リークの検出)
  • optionally run tests that match a regexp(オプションで正規表現に一致するテストの実行)
  • auto-exit to prevent “hanging” with an active loop("ハング"防止のためのアクティブループ自動終了)
  • easily meta-generate suites & test-cases(テストスイートやテストケースを簡単にメタ生成)
  • mocha.opts file support(mocha.optsファイルのサポート)
  • mocha-debug(1) for node debugger support(Nodeデバッガー用のmocha-debug(1) のサポート)
  • detects multiple calls to done()(done()の複数呼び出しの検出)
  • use any assertion library you want(好きなアサーションライブラリを使える)
  • extensible reporting, bundled with 9+ reporters(拡張可能なレポート。9種類のレポートをバンドル)
  • extensible test DSLs or “interfaces”(拡張可能なテストDSLまたは"インターフェース")
  • before, after, before each, after each hooks(befor, after, before each などのフックメソッド)
  • coffee-script support(cofee-scriptのサポート)
  • TextMate bundle(TextMateをバンドル)
  • and more!(他にもまだあるよ!)

Installation

ここは特に書くことがないです。普通にnpmでインストールします。

npm install -g mocha

Assertions

Mochaでは好きなアサーションライブラリを使うことができます。

本エントリのサンプルコードではMochaのチュートリアルに従ってshould.jsを使います。

Synchronous code

こんなコードがあったとして

exports.add = function(a, b) { return a + b; }

テストはこう書けます。

var example = require('../lib/example.js')

describe('example - Synchronous code', function(){
  describe('#add()', function(){
    it('should return \'a + b\' when the values are \'a\' and \'b\'.', function(){
      example.add(1, 2).should.equal(3);
      example.add(2, 3).should.equal(5);
    })
  })
})

Asynchronous code

結果が得られるまで時間がかかるコードのテストの場合、

exports.waitAndAdd = function(a, b) {
  var ev = new EventEmitter(); 
  setTimeout(function(){
    ev.emit('sum', a + b); 
  }, 1000);
  return ev; 
}

done()が呼ばれるまで待機して、done()呼び出しの後に次のテストが実行されます。

var example = require('../lib/example.js');

describe('example - Asynchronous code', function(){
  describe('#waitAndAdd()', function(){
    it('should return \'a + b\' when the values are \'a\' and \'b\'.', function(done){
      var ev = example.waitAndAdd(1, 2);
      ev.on('sum', function(sum) {
        sum.should.equal(3);
        done();
      });
    })
  })
})

テスト実行前に呼ばれるフックメソッドも書けます。

describe('example - hooks', function(){
  var beforeCount = 0,
      beforeEachCount = 0,
      afterEachCount = 0,
      afterCount = 0;

  before(function(done) {
    // 全テスト実行前に1回呼ばれる。
    beforeCount++;
    done();
  })

  beforeEach(function(done){
    // 各テスト実行前に毎回呼ばれる。
    beforeEachCount++;
    done();
  })

  afterEach(function(done) {
    // 各テスト実行後に毎回呼ばれる。
    afterEachCount++;
    done();
  });

  after(function(done) {
    // 全テスト実行後に1回呼ばれる。
    afterCount++;
    afterCount.should.equal(1);
    done();
  })

  describe('test for hook methods.', function(){
    it('test1', function(){
      beforeCount.should.equal(1);
      beforeEachCount.should.equal(1);
      afterEachCount.should.equal(0);
    })
    it('test2', function(){
      beforeCount.should.equal(1);
      beforeEachCount.should.equal(2);
      afterEachCount.should.equal(1);
    })
    it('test3', function(){
      beforeCount.should.equal(1);
      beforeEachCount.should.equal(3);
      afterEachCount.should.equal(2);
    })
  })
})

Pending tests

まだ書いていないけど後で書くつもりのテストは、こう書いておけます。

describe('example - pending test', function(){
  describe('#minus()', function(){
    it('should return \'a - b\' when the values are \'a\' and \'b\'.')
  })
})

mochaを実行すると、itに書いた「should return 'a - b' when the values are 'a' and 'b'.」が画面上にも出力されます。

Test duration

テスト結果が非常に見やすい!

オプションに --reporter spec を付けて実行した結果は以下のようになります。

mocha

赤字で「(1003ms)」と表示されているのは、実行完了までに時間がかかっているテスト、青字で「should return 'a - b' when the values are 'a' and 'b'.」と表示されているのは、前述のPending testsです。

mocha(1)

オプションは以下のとおりです。

-w, —watch

テストが変更されるまでずっと待機して、変更を検知するとテストを実行してくれます。

実行時点で存在するテストのみが監視対象で、新しく作ったテストを監視するには再実行する必要があるようです。微妙に使いづらい…

—globals <names> 

グローバル変数名を渡せます。複数指定する場合はカンマで区切ります。

例えば、「--globals myname,global1Called」を付けて以下の二つのテストを実行すると、テストコードの中でmyname, global1Calledという二つのグローバル変数を扱えます。

describe('example - global option 1', function(){
  before(function(done) {
    myname = 'ryu22e';
    global1Called = true;
    done();
  });
  describe('test for global variables.', function(){
    it('myname is \'ryu22e\'.', function(){
      myname.should.equal('ryu22e');
      global1Called.should.equal(true);
    })
  })
})
describe('example - global option 2', function(){
  describe('test for global variables.', function(){
    it('myname is \'ryu22e\'.', function(){
      myname.should.equal('ryu22e');
      global1Called.should.equal(true);
    })
  })
})

いまいち用途が分からないのですが、テストAをパスしたらグローバル変数フラグをtrueにして、テストBはグローバル変数フラグがtrueでないと実行しない、という使い方ができるような気が。

—ignore-leaks

テストコードに--globalsで指定されていないグローバル変数があってもエラーが出ないようになります。

-r, —require <name>

テストコードで利用するモジュール名を指定すると、テストコード中でrequireを書かなくてもモジュールを扱えます。

-u, —ui <name>

bdd, tdd, exportsの何れかから、テストコードの書き方を選べます。

デフォルトはbdd。

-R, —reporter <name>

テスト実行結果の表示形式を選べます。

デフォルトはdot。

-t, —timeout <ms>

テストケースがタイムアウトする時間を指定できます。

デフォルトは2000ミリ秒。

-s, —slow <ms>

「遅いテスト」と判断する基準になる時間を指定できます。

実行終了にかかった時間がここで指定した時間より遅い場合に、当該テストがハイライト表示されます。

デフォルトは75ミリ秒。

-g, —grep <pattern>

patternに指定した正規表現と一致するdescribeやitが書かれているテストのみが実行されます。

mocha-debug(1)

テストのステップ実行ができるらしいけど、詳細は未調査。

Interfaces

Mochaではテストの書き方をbdd, tdd, exportsの何れかから選ぶことができます。

書き方の具体例は省略(Mochaのチュートリアルを見てください)。


続きは後編



ryu22e at 23:16コメント(0)トラックバック(0)プログラム | Javascript 

トラックバックURL

コメントする

名前
 
  絵文字
 
 
Profile

ryu22e

Recent Comments
  • ライブドアブログ