Node.js
test
mongoose
mocha

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

More than 1 year has 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やっとくとテスト好きになってすごくいい。
  • クライアントサイドのテストコードも読みたい。