テストフレームワークは、busterJSが一番慣れてたんですが、
部署御推薦のmochaをいっちょやってみるかー、と思っているこのごろです。
そしてついでに、
「このへんは、node.jsの場合どうテストするのがかっこいいかしら!!」
と気になっていた部分について、いろいろ調べてみました。
mochaの細かい説明については、公式その他をみてください。
mochaっていうかほとんどshouldですよね。
普通のテスト(libraryやcontrollerのテスト)
テストしたいmoduleをrequireして、
shouldもrequireして、テストを書く。これが基本ですね。
// テストしたいmoduleをrequire
var hoge = require('../hoge');
// テスト用のライブラリをrequire (mochaの場合は、shouldがあればいいはず)
var should = require('should');
// あとはテストを書くのみ
describe('hoge', function () {
it('足し算をする', function () {
hoge.add(2, 3).should.equal(5);
});
it('割り算をする', function () {
hoge.divide(6, 3).should.equal(2);
});
it('ゼロで割ったらぬるぽ', function () {
// falseやnullを確認するときはこんな感じ
should.not.exist(hoge.divide(5, 0));
});
});
しかし、こうシンプルに書けない場合、
nodeのコードの中だけで完結しないようなコードは、どうテストするんだろう?
と気になってたわけです。
express serverをテスト
たとえば、express serverのルーティング。
テストコード内で実際にサーバーを起動させて、
実際にURLを叩いてテストする…? とか思ってたんですが、
ここでいい感じのサンプルを見つけました。
簡単に書くと以下。
// expressのroutesのみrequire
var routes = require('../routes');
require('should');
describe('routes', function () {
var req, res;
beforeEach(function () {
// 簡単に、フェイクのreqとresを用意
req = {};
res = {
redirect: function () { },
render : function () { }
};
});
describe('index', function () {
it('should display index page with title', function (done) {
// res.render、すなわち表示用に呼ばれる関数をフェイクで作成
// 事実上のcallback関数
res.render = function (view, vars) {
// 'index'というviewを使うことを確認
view.should.equal('index');
// 変数'title'が'Express'になっていることを確認
vars.title.should.eql('Express');
// テスト終了
done();
};
// 実際に、routesのindexを実行 = アクセスしてみる
routes.index(req, res);
});
});
});
ポイントは、
- expressの本体(app.js)ではなく、routesのみrequireすること
- res.renderをその場で書くことで、callback関数のような役割になる
ということ。
考えてみれば、expressは本体がかなり薄ーく作ってあるので、
routesだけテストすれば、ほぼサーバー自体のテストになるわけなんですね。
(urlとroutesの対応とかは、これでは見れてないけど)
あと、ここまで書いた後に改めて調べたら、
ちゃんとexpress-testというnpmもあったので、
そちらを使った方が素直なのかもしれませぬ。
cliをテスト
nodeで書くものといえばサーバー、あるいはコマンドラインツールですよね。
コマンドラインツールも、実際に実行してテストした方がいい…? とかいろいろ悩んでました。
これに関しては、npmで"cli"keywordがついてるやつをコードリーディングしてみました。
が、どうやらあんまりテスト書かれてない…?
foreverとかhttp-serverとかgrunt-cliとか、
"cli"の有名どころを読んでみたんですが、
本体のテストだけで、コマンドラインのインタフェース部分についてはテストされていない感じでした。
(gurnt-cliに関しては、全体通してもjshintしかしてない)
そ、そういうもんなのかな…?
とりあえず、
- cliの部分をできるだけ薄くして、オプション付け方とかまでどこかで書いてテストも書くとか、
- child_processで実際に実行してテストするとか、
ですかね。
また実際にやってみて考えます。
mongooseなモデルをテスト
これはなんてこたぁない普通のテストだと思うんですが。
ただ、しばらくPerlばっかり書いてる時期があった名残で、
dbの初期化とかもかっこよく書きたい。
もっと言うと、node-configからtest用のconnect情報取って、つなぐとこまでは共通化して書きたい。
とか思ったので、test initスクリプトみたいなのを書いてみました。
process.env.NODE_ENV = 'test';
var mongoose = require('mongoose'),
config = require('config'),
async = require('async');
require('should');
var test = function (initModels) {
before(function (done) {
async.series([function (next) {
mongoose.connect(config.db.url, next);
}, function (next) {
async.forEach(initModels || [], function (model, next) {
mongoose.model(model).remove(next);
}, next);
}], done);
});
};
module.exports = test;
これで、テストコード本体で以下のように呼び出すだけで、
require('lib/test')(['User']);
- envを'test'に設定
- configをenvで出し分けしてあるので、test用のconfig情報が読まれる
- beforeの中で、connectとdb初期化を実行
- ので、テストコード中ではそれらを意識しなくても勝手にやってくれる
といかんじでかっこいいです。
しかしbefore便利ですね。
テストコード中の非同期ネストが確実に減らせてうれしい。
その他
- テストに使う固定のファイルは、
test/fixture
に置いているケースが多かった。
まとめ
- mochaいい。
- Perlやっとくとテスト好きになってすごくいい。
- クライアントサイドのテストコードも読みたい。