Heim >Web-Frontend >js-Tutorial >Eine kurze Analyse verwandter Probleme und Lösungen mithilfe der Abhängigkeitsinjektion in Node.js_node.js
Kürzlich habe ich mich der Verwendung von Dependency Injection zugewandt, um zu verstehen, wie sich Code auf einfache Weise trennen und beim Testen unterstützen lässt. Module in Node.js sind jedoch auf die von Node bereitgestellte System-API angewiesen, was es schwierig macht zu beurteilen, ob private Abhängigkeiten ordnungsgemäß verwendet werden. Eine allgemeine Abhängigkeitsinjektion ist in diesem Fall schwierig anzuwenden, aber geben Sie die Hoffnung noch nicht auf.
erforderlichVerursacht Problem
Node.js kann bei Bedarf problemlos Abhängigkeiten importieren. Es funktioniert sehr gut und ist einfacher als AMD-Schemalader wie RequireJS. Das Problem entsteht, wenn wir diese Abhängigkeiten verspotten. Wenn das Laden von Modellen in Node.js kontrolliert wird, wie können wir dann die beim Testen verwendeten Pseudoobjekte kontrollieren? Wir können den VM-Modus von Node verwenden und über VM das Modell in einen neuen Kontext laden. In einem neuen Kontext können wir steuern, wie Anforderungen in den Methoden des Modells berücksichtigt werden.
Lösung
Danke für diesen Artikel, ich kann Ihnen jetzt eine ziemlich gute Lösung anbieten:
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; };
Sie können das Code-Snippet auch hier herunterladen Obwohl der Code nicht in den meisten Fällen veröffentlicht wird, könnte er dennoch eine Erklärung gebrauchen. Wenn wir testen, möchten wir dieses Modul in den Test laden , mit der Funktion „loadModule“ Modultest laden statt „require“.
Der erste Parameter, filePath, gibt den Suchort an, an dem wir das Modell testen möchten. Der zweite Parameter, „Mocks“, enthält ein Objekt, dessen Eigenschaftsnamen mit den Namen des Modells übereinstimmen, das wir anfordern möchten. Die durch diese Attribute angegebenen Werte sind Pseudoobjekte, die zum Ersetzen der allgemein erforderlichen Modelle verwendet werden.
Im Wesentlichen wird vm verwendet, um das Modell in einem anderen „Kontext“ zu laden und auszuführen. Mit anderen Worten: Wir erstellen globale Variablen (wie „require“ und „exports“) neu, damit wir sie steuern können. Beachten Sie, dass wir eine neue Anforderungsfunktion geschrieben haben, die verfügbar ist. Es prüft lediglich, ob es eine Scheinabhängigkeit für den Ausführungsnamen gibt, und wenn ja, delegiere ich sie an die übliche Require-Funktion.
Beispiel für die Verwendung des Modulladers
Wenn Sie immer noch etwas verwirrt sind, können Sie sich das Codebeispiel unten ansehen, um zu sehen, wie es im Kontext verwendet wird, was Ihnen helfen kann, es klarer zu machen. Zuerst erstellen wir ein einfaches Modul.
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` }); });
Das Wichtigste ist, dass wir in den Zeilen 7 bis 12 ein Dummy-Objekt für fs erstellen und unsere neue LoadModule-Funktion verwenden, um dieses verwendete Objekt in das kleine Modul oben einzubinden (ich meine großartig! Denken Sie daran, was großartig ist, Rechts?).