mocha+chai+blanketを使ってcoffeescriptでBDD

最近空いた時間にhubotのadapterを開発してるのですが、coffeescriptでテストを書きたくなったときにちょっと試行錯誤したので備忘録として残しておきます。

目標

mochaとchaiでBDD

セットアップ

mochaはjs用のテストフレームワークで、C/Sのどちらでも動作します。他のアサーションライブラリと組み合わせて使う必要があるため、今回はBDDアサーションライブラリであるchaiを使います。

まずpackage.jsonに以下を追記します。

{
  "scripts": {
    "test": "mocha test"
  }
}

次に必要なmoduleをインストールします。

npm install coffee-script -g
npm install chai --save-dev
npm install mocha --save-dev

次にtest/mocha.optsというファイルを作ります。 これはmochaに渡すコマンドライン引数をまとめたものです。 直接引数として渡しても問題ないのですが、長くなりがちなのでこっちの方がのちのち便利かな思います。

--compilers coffee:coffee-script

テストコード

chaiにはTDDスタイルのassertを使う方法と、BDDスタイルのshould/expectを使う方法があります。 個人的にはshouldスタイルが好きなので以下はshoudスタイルの例。

# test/sample.test.coffee
should = (require 'chai').should()

describe 'String', ->
  describe '#concat()', ->
    it 'should return "John Doe"', ->
      # assert values
      'foo'.concat('bar').should.equal 'foobar'

  describe '#split()', ->
    it 'should return [1,2,3]', ->
      # assert arrays and objects
      'foo,bar,baz'.split(',').should.deep.equal ['foo', 'bar', 'baz']

describe 'Tweet', ->
  # async test
  it 'should post', (done) ->
    tweet = new Tweet 'Hello!'
    tweet.post 'Hello!', (err, res) ->
      done()

その他いろいろな使い方はAPIリファレンスを参照。

sinonと組み合わせて使うのも簡単です。

ちなみに上に書いたコードの中でdescribeitといった枠組みを提供しているのがmochaです。beforeafterなどもあります。

テストの実行

npm test

blanketを使ったカバレッジ測定

もともとcoverallsでカバレッジ測定したかったので、以下の要件でライブラリを探してました。

そこで試したのがibrik(coffeescript用istanbul)とblanketだったのですが、ibrikの方はパーサーがエラーで動かず。 結局blanketを使うことにしました。

セットアップ

package.jsonに以下を追記します。

{
  "config": {
    "blanket": {
      "pattern": "src",
      "loader": "./node-loaders/coffee-script",
      "data-cover-never": "node_modules"
    }
  }
}

上記のpatternにカバレッジ計測対象のファイルにマッチするパターンを書きます。 このpatternなんですが、上の例だとフルパスにsrcが含まれてる場合(/Users/akiomi/src/hoge-project/など)はプロジェクト直下のファイルがすべて含まれてしまったので"pattern": "hoge-project/src"とするなどちょっと工夫が必要です。

次はblanketのインストール。

npm install blanket@">=1.1.5" --save-dev

blanketの古いバージョン(1.1.4以前)ではpackage.jsonのscriptsの中に設定を書かなければいけなかったのですが、新しいnpmを使っているとpublish時にscriptsに文字列以外が含まれているエラーとなってしまいます。 blanketの1.1.5からはconfigに設定を書くことが推奨となりこの問題が解決されたので、npm publishを行う場合には1.1.5以上を使用した方がよさそうです。 また原因がわからないのですが、travis-ciでビルドする場合には1.1.5を指定してもうまく動かなかったため、1.1.6が出るまではgitのコミットハッシュを指定するなどして対応が必要です。

そしてmochaからblanketを呼び出すために以下をtest/mocha.optsに追記。

--require blanket

ここで、カバレッジ測定しやすくするためにMakefileを作ります。 本当はgruntを使いたかったんですが、travis-ciでgrunt-mocha-covを使って動かすことができず断念…(ローカルの実行ではうまくいくんですが)。

MOCHA = ./node_modules/.bin/mocha
.PHONY: test

test:
    $(MOCHA) test

test-coverage:
    $(MOCHA) -R html-cov test > coverage.html

カバレッジ計測

以下のコマンドでテストとカバレッジ測定が出来ます。

make test # テスト
make test-coverage # カバレッジ計測

あとpackage.jsonの方のscripts.testも変更しておきます。

{
  "scripts": {
    "test": "make test"
  }
}

travis-ciとcoveralls

githubリポジトリを公開している場合はtravis-ciによるビルドとcoverallsによるカバレッジ計測が簡単にできます。 travis-ciとcoverallsの設定は省略。

まずcoverallsでのカバレッジ計測用のmoduleをインストール。

npm install mocha-lcov-reporter --save-dev
npm install coveralls --save-dev

次にMakefiletravis-ci用のビルドタスクを追加。

test-coveralls:
    mocha test --reporter mocha-lcov-reporter | coveralls

あとはプロジェクトルートに.travis.ymlを作るだけ。

language: node_js
node_js:
  - "0.10"
script: make test-coveralls

これでリポジトリにpushした段階で勝手にtravis-ciがビルドしてくれて、ビルド結果をcoverallsに連携してくれます。キーやトークンの設定とかも特に必要ありません。

感想とか

今回試したいろいろなライブラリは成熟してないものが多くてハマりポイントが多かったです。 全体的にまだいいやり方があると思うので、もうちょっと試行錯誤してみようと思います。

参考になるかはわかりませんが、以下のリポジトリで実践しています。