これは Hubot Advent Calendar 2014 の 22 日目の記事です。
また、今回は @bouzuya の Hubot 連載の第 14 回です。目次は、第 1 回の記事にあるので、そちらをどうぞ。
前回まで、そして今回は
前回は いまさらだけど Hubot のドキュメントを読んでみよう ということで、Hubot のドキュメントについて紹介しました。
今回は Hubot スクリプトのテストについて紹介します。とても軽く触れるだけですが。
変にバズってぼくのリポジトリで最も多くスターがついてしまった bouzuya/hubot-twada ライクな Hubot スクリプトをつくりましょう。
ちなみに今回から generator-hubot
は 0.2.0
にバージョンアップしています。連載中にどんどんバージョンアップされますね。
とりあえず grunt test
いつものように yo hubot:script
しましょう。今回は hubot-t_wada
をつくります。
$ mkdir hubot-t_wada
$ cd hubot-t_wada
$ yo hubot:script
...
とりあえず grunt test
してみてください。たぶん動きません。
grunt が見つからない
$ grunt
zsh: command not found: grunt
もし grunt
が見つからないなら grunt-cli
を global にインストールしてください。
$ # 必要なら `sudo` をつけてください
$ npm install -g grunt-cli
coffe-script が見つからない
$ grunt test
Running "mochaTest:test" (mochaTest) task
Warning: Cannot find module 'coffee-script' Use --force to continue.
Aborted due to warnings.
もし coffee-script
が見つからないなら coffee-script
をインストールしてください。
generator-hubot
は、coffee-script
が global にインストールされている前提になっています。個人的には coffee-script
を global にインストールはしないので、 PR を投げたのですが、無視されています ので、テストコマンドは動きません。
おとなしく devDependencies
に coffee-script
をインストールしましょう。global にインストールしたいならお好きにどうぞ。
また、余計なトラブルを防ぎたいなら hubot
が依存している coffee-script
にバージョンを合わせる (1.6.3
) のが吉です。
$ npm install --save-dev [email protected]
今度こそ grunt test
これでようやくテストが動くはずです。
$ grunt test
Running "mochaTest:test" (mochaTest) task
t-wada
✓ registers a respond listener
✓ registers a hear listener
2 passing (9ms)
Done, without errors.
grunt test:watch
じゃあ、書いていきましょう。
書き換えごとに grunt test
するのも面倒なので、grunt test:watch
してファイルの変更を監視しましょう。
$ grunt test:watch
ファイルを変更(保存)するごとに grunt test
が動きます。
サンプルで書いてあるテストは「これインタフェースじゃなく実装をテストしてねえか?」って感じで、ぼくにコードレビューを投げてきたらリジェクトしたいんですけど、せっかく generator-hubot が提示してくれているのでその流儀に従います。
chai = require 'chai'
sinon = require 'sinon'
chai.use require 'sinon-chai'
expect = chai.expect
describe 't-wada', ->
beforeEach ->
@robot =
respond: sinon.spy()
@msg =
send: sinon.spy()
require('../src/t-wada')(@robot)
@robot.respond.firstCall.args[1](@msg)
it 'registers a respond listener', ->
expect(@robot.respond).to.have.been.calledWith(/テスト書いてない/)
it 'sends a message', ->
expect(@msg.send).to.have.been.calledWith(
'テスト書いてないとかお前それ @t_wada の前でも同じこと言えんの?')
こんな感じになりました。正直、解説が面倒です。
Node.js におけるテストでおなじみの mocha
と chai
と sinon.js
(+sinon-chai
) の API を理解していればなんてことはないですね。mocha はテストフレームワーク、chai はアサーションライブラリ、 sinon.js はテストダブルライブラリです。
- mocha http://mochajs.org/
- chai http://chaijs.com/
- sinon.js http://sinonjs.org/
どれも Node.js 的にはかなりメジャーなライブラリなので情報そのへんにあるでしょう (適当) 。
generator-hubot
が生成する環境
まとめると generator-hubot
が生成する環境は
- grunt
- mocha
- chai
- sinon
な環境です。
実は Hubot のテストに標準的な方法はない
以下、私見です。
まず Hubot のテストに標準的な方法はないと言って良いと思います。
ぼく自身もいろいろ模索していて、過去にいろいろ書いています。軽く経緯に触れます。
hubot-mock-adapter
まずは blalor/hubot-mock-adapter です。
これは mock 用のアダプターです。e2e テストに近いので、良いのですが準備が相当面倒です。
kakashi
個人的に bouzuya/kakashi という hubot-mock-adapter のラッパーを書いたこともありました。仕様が複雑なので、使わない方が良いです。
ちなみに bouzuya/hubot-url などで使っているようですね。
regex / callback
上記のような e2e テストは実際はやりづらいです。例えば Hubot のメッセージが処理されるタイミングは非同期な上に完了したかどうかも取れません。テストが失敗するときはテストフレームワークのタイムアウトまで待たされます。タイムアウトを短くするなどしてもいいでしょう。ただ、もうぼくは諦めています。
ぼくはテストを書くときは、登録済みの listener をバラして、regex
と callback
をテストするスタイルを取っています。
ちなみに bouzuya/hubot-twada などで使っているようですね。
現状の @bouzuya 環境
現状の @bouzuya 環境を確認したいなら bouzuya/hubot-script-boilerplate を見ると良いです。
一覧にするとこんな感じ。
- gulp
- mocha
- power-assert
- sinon
- istanbul
- coffee-script
すこしテスト時のビルドが複雑です。また本家の CoffeeScript のバージョンがいつまでも 1.6.3 なのが気に入らないので、もうコンパイル済みの JavaScript をコミットしています。
理想の環境をつくるなら
上記のものに近い、理想的な環境を手っ取り早く作りたいなら、sanemat/generator-hubot-script-gulp は素晴らしい選択肢です。ぼくの知る限り、Hubot スクリプト向けの Yeoman ジェネレーターではもっとも優れています。
まとめ
ざっと Hubot スクリプトのテストやその環境について触れてみました。
今回のサンプルは bouzuya/hubot-t_wada にあります。参考にどうぞ。
最後に
正直に言うなら Hubot のテストなんて適当で良いと思います。3 行のスクリプトに山ほどテストを書いて何が楽しいんだと。何のためにテストするのか、本当に必要なのかを考えるべきだと思います。きちんと、コストやリターンなどを考えてテストすべきです。 Hubot スクリプトに限らず。