最近、私はコードを分離してテストを支援する簡単な方法を理解するために Dependency Injection の使用に目を向けました。ただし、Node.js のモジュールは Node が提供するシステム API に依存しているため、プライベートな依存関係が適切に使用されているかどうかを判断するのが困難です。この場合、一般的な依存性注入を使用するのは困難ですが、まだ希望を捨てないでください。
require問題の原因
Node.js は、必要に応じて依存関係を簡単にインポートできます。これは非常にうまく機能し、RequireJS などの AMD スキーマ ローダーよりもシンプルです。問題は、これらの依存関係を模擬するときに発生します。 Node.js でのモデルの読み込みが制御されている場合、テスト中に使用される疑似オブジェクトをどのように制御できるでしょうか? Node の vm モードを使用でき、vm を通じて新しいコンテキストでモデルをロードできます。新しいコンテキストで実行すると、要件がモデルのメソッドにどのように反映されるかを制御できます。
解決策
この記事のおかげで、非常に優れた解決策を提供できるようになりました。コードは以下のとおりです。
var vm = require('vm'); var fs = require('fs'); var path = require('path'); /** * Helper for unit testing: * – load module with mocked dependencies * – allow accessing private state of the module * * @param {string} filePath Absolute path to module (file to load) * @param {Object=} mocks Hash of mocked dependencies */ exports.loadModule = function(filePath, mocks) { mocks = mocks || {}; // this is necessary to allow relative path modules within loaded file // i.e. requiring ./some inside file /a/b.js needs to be resolved to /a/some var resolveModule = function(module) { if (module.charAt(0) !== '.') return module; return path.resolve(path.dirname(filePath), module); }; var exports = {}; var context = { require: function(name) { return mocks[name] || require(resolveModule(name)); }, console: console, exports: exports, module: { exports: exports } }; vm.runInNewContext(fs.readFileSync(filePath), context); return context; };
コード スニペットは こちら からダウンロードすることもできます。コードは記事にはほとんど掲載されていませんが、テストするときにこのモジュールをテストにロードする必要がある可能性があります。 、loadModule関数を使用して、requireの代わりにモジュールテストをロードします。
最初のパラメータ filePath は、モデルをテストする検索場所を指定します。 2 番目のパラメーター、mocks には、要求しようとしているモデルの名前とプロパティ名が一致するオブジェクトが含まれています。これらの属性で指定された値は擬似オブジェクトであり、一般に必要なモデルを置き換えるために使用されます。
基本的に、vm を使用してモデルをロードし、別の「コンテキスト」で実行します。言い換えれば、グローバル変数 (require やexports など) を再作成して、それらを制御できるようにします。利用可能な新しい require 関数を作成したことに注意してください。実行名に疑似依存関係があるかどうかを確認するだけです。存在する場合は、それを通常の require 関数に委譲します。
モジュールローダーの使用例
まだ少し混乱している場合は、以下のコード例を見てコンテキストでどのように使用されているかを確認すると、より明確になる可能性があります。まず、簡単なモジュールを作成します。
var fs = require('fs'); module.exports = { // Do something with `fs` } 想象一下这个很酷,对吗?不管怎样,现在我们测试那个模块,但是我们要模拟fs来看看它是怎么在内部使用的。 // Jasmine's syntax http://pivotal.github.com/jasmine/ describe('someModule', function() { var loadModule = require('module-loader').loadModule; var module, fsMock; beforeEach(function() { fsMock = { // a mock for `fs` }; // load the module with mock fs instead of real fs module = loadModule('./web-server.js', {fs: fsMock}); }); it('should work', function() { // a test that utilizes the fact that we can now control `fs` }); });
注目すべき主な点は、7 行目から 12 行目で、fs のダミー オブジェクトを作成し、新しいloadModule 関数を使用して、この使用済みオブジェクトを上の小さなモジュールに結び付けていることです (これは素晴らしいという意味です! 覚えておいてください、これは素晴らしいことです)右?)。