Edited at

node.jsでこんなのもテストしたい!! という話

More than 3 years have passed since last update.

テストフレームワークは、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スクリプトみたいなのを書いてみました。


lib/test.js

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やっとくとテスト好きになってすごくいい。

  • クライアントサイドのテストコードも読みたい。