📝
newが絡むオブジェクトをモック化してテストする
JavaScriptテスト・フレームワークJasmineにはSpy, Mock機能が備わっており、抽象度の高いテストが書けます。JavaScriptテスト・フレームワークにも色々ありますが、私はこれが気に入っています。
ところでJavaScriptにはnew ClassName()
という記法があります。
lib/controllers/todos.js
'use strict';
var mongoose = require('mongoose'),
Todo = mongoose.model('Todo');
exports.create = function (req, res, next) {
var newTodo = new Todo(req.body);
newTodo.save(function(err) {
if (err) return res.json(400, err);
});
return res.send(200);
};
exports.show = function (req, res, next) {
Todo.find({}, function (err, todos) {
res.send(todos);
});
};
先日の拙記事「MEAN Stackアプリケーション 最初の一歩」からの引用ですがnew Todo()
という記述があります。このnew
がテストをする上で大変厄介だと分かりました。
Spyオブジェクトを生成するにしても、new
について一体なんのメソッドを監視すればよいのか。Todo
のモック化はどうすればいいのか…。散々悩みました。
モック化
Todo
には、Todo.find()
と、new Todo()
して生成されたインスタンスが呼ぶinstance.save()
というメソッドがあります。「クラスメソッド」と「インスタンスメソッド」の関係です。これをJasmineのSpyでモック化します。
spec.js
// ...
var di, Todo;
beforeEach(function(){
Todo = function(){return;};
Todo.find = function(){return;};
Todo.prototype.save = function(){return;}
spyOn(Todo, 'find');
spyOn(Todo.prototype, 'save');
di = {Todo: Todo};
});
// ...
Todo =
したあとTodo.find =
とする辺りがなかなか気持ち悪いですが、動きます。あとはdi
(Dependency Injection)変数にまとめて格納して、テスト時には中のTodo
コンストラクタを使わせます。
Node.jsにおいて、本番コードではTodo = mongoose.model('Todo');
を行い、テストではdi
内のTodo
を使うという分岐の実装は、書けるには書けるんですがやたらと冗長なので今回は割愛します。モック化はブラウザ向けに書かれたJavaScriptへのテストでも使えるテクニックかと思います。
--
書けば書くほどJavaScriptって奇妙ね!
- 14/11/21追記: mocha + Sinon.JS テストダブル集
Discussion