📝

newが絡むオブジェクトをモック化してテストする

2020/10/27に公開

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って奇妙ね!

Discussion