A brief analysis on the problems and solutions of dependency injection in ES0en.ES1en

  • 2020-06-19 09:45:59
  • OfStack

More recently, I've turned to dependency injection to help understand the easy way to separate code and to help with testing. However, the modules in ES1en. js rely on API, the system provided by Node, which makes it difficult to judge whether private dependencies are being used properly. Dependency injection 1 is hard to use in this situation, but don't give up hope for now.

requireCauses problem

Node. js easily imports dependencies according to requirements. It works well and is simpler than AMD schema loaders such as RequireJS. The problem comes when we simulate those dependencies. If the loading of the model in ES14en.js is controlled, what can we do to control that the dummy objects are used during testing? We can use Node's vm schema, through which we can load the model in a new context. Running in the new context, we can control how requirements reflect the model.

The solution

Thanks for this article, I can now provide you with a fairly good solution. The code is as follows:


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;
};

You can also download the code snippet here. Although it is not the most published code in the article, it can still use 1 explanation. When we test, we will load this module into the test, using theloadModulefunction instead of ofrequire to load the module test.

The first parameter, filePath, specifies where we want to test the model for lookup. The second parameter, mocks, contains an object whose property name matches the name of the model we are trying to require. The values specified by those attributes are pseudo-objects, which are used in place of the 1 commonly used require model.

Essentially, vm is used to load and run the model in another "context." In other words, we reconstruct global variables (such as require and exports) so that we can control them. Note that we have written a new require function that is available. All I do is check to see if the name executed under 1 has a simulated dependency, and if so, I delegate it to the usual require function.

An example of using a module loader

If you're a little confused, you might want to look at the following code example to see how it's used in context. First, we create a simple module.


var fs = require('fs');
 
module.exports = {
// Do something with `fs`
}
 imagine 1 This is cool, isn't it? Anyway, now we're testing that module, but we're going to simulate it, right fs Let's see how it's used internally. 
 
// 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`
});
});

The main thing to notice is that between lines 7 and 12, we created a dummy object for fs and used our new loadModule function to associate the object being used with the small module above (I mean cool! Remember, this is awesome, right?) .


Related articles: