145
141

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2013-04-30

テストフレームワークは、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やっとくとテスト好きになってすごくいい。
  • クライアントサイドのテストコードも読みたい。
145
141
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
145
141

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?