Posted at

Hubot スクリプトのテストやその環境を知ろう

More than 3 years have passed since last update.

これは Hubot Advent Calendar 2014 の 22 日目の記事です。

また、今回は @bouzuya の Hubot 連載の第 14 回です。目次は、第 1 回の記事にあるので、そちらをどうぞ。


前回まで、そして今回は

前回は いまさらだけど Hubot のドキュメントを読んでみよう ということで、Hubot のドキュメントについて紹介しました。

今回は Hubot スクリプトのテストについて紹介します。とても軽く触れるだけですが。

変にバズってぼくのリポジトリで最も多くスターがついてしまった bouzuya/hubot-twada ライクな Hubot スクリプトをつくりましょう。

ちなみに今回から generator-hubot0.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 を投げたのですが、無視されています ので、テストコマンドは動きません。

おとなしく devDependenciescoffee-script をインストールしましょう。global にインストールしたいならお好きにどうぞ。

また、余計なトラブルを防ぎたいなら hubot が依存している coffee-script にバージョンを合わせる (1.6.3) のが吉です。

$ npm install --save-dev coffee-script@1.6.3


今度こそ 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 が提示してくれているのでその流儀に従います。


test/t-wada-test.coffee

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 におけるテストでおなじみの mochachaisinon.js (+sinon-chai) の API を理解していればなんてことはないですね。mocha はテストフレームワーク、chai はアサーションライブラリ、 sinon.js はテストダブルライブラリです。

どれも 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 をバラして、regexcallback をテストするスタイルを取っています。

ちなみに 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 スクリプトに限らず。