6

I am currently using requirejs to manage module js/css dependencies. I'd like to discover the possibilities of having node do this via a centralized config file. So instead of manually doing something like

define([    
'jquery'
'lib/somelib'
'views/someview']

within each module.

I'd have node inject the dependencies ie

require('moduleA').setDeps('jquery','lib/somelib','views/someview')

Anyway, I'm interested in any projects looking at dependency injection for node.

thanks

Matthieu Napoli
  • 48,448
  • 45
  • 173
  • 261
Chin
  • 12,582
  • 38
  • 102
  • 152
  • Dependency injection is not for dynamic languages. – Justin Thomas Dec 02 '11 at 06:39
  • @justin - thanks but I beg to differ. Maybe the term is incorrect, but isn't what I'm talking about dynamic? – Chin Dec 02 '11 at 07:41
  • 1
    No, dynamic languages are that way by nature and don't need DI. The point of DI is to get around that limitation with static typing. – Justin Thomas Dec 02 '11 at 07:51
  • "inject require data dynamically from the server" http://clux.github.com/modul8/ – Chin Dec 02 '11 at 10:27
  • The recent post Javascript has a Built-In Dependency Injection Framework is very relevant: http://caines.ca/blog/programming/javascript-has-a-built-in-dependency-injection-framework/ – Ruben Verborgh Apr 03 '13 at 11:11
  • Please try this one: https://github.com/robo-creative/nodejs-robo-container – Robo Jul 11 '17 at 07:38

3 Answers3

6

I've come up with a solution for dependency injection. It's called injectr, and it uses node's vm library and replaces the default functionality of require when including a file.

So in your tests, instead of require('libToTest'), use injectr('libToTest' { 'libToMock' : myMock });. I wanted to make the interface as straightforward as possible, with no need to alter the code being tested. I think it works quite well.

It's just worth noting that injectr files are relative to the working directory, unlike require which is relative to the current file, but that shouldn't matter because it's only used in tests.

Nathan MacInnes
  • 11,033
  • 4
  • 35
  • 50
  • Nice lib. I made one some time ago :) https://github.com/lukeschafer/modulemock never really continued development on it – Luke Schafer Jul 03 '12 at 02:05
5

I've previously toyed with the idea of providing an alternate require to make a form of dependency injection available in Node.js.

Module code

For example, suppose you have following statements in code.js: fs = require('fs');

console.log(fs.readFileSync('text.txt', 'utf-8'));

If you run this code with node code.js, then it will print out the contents of text.txt.

Injector code

However, suppose you have a test module that wants to abstract away the file system.
Your test file test.js could then look like this:

var origRequire = global.require;
global.require = dependencyLookup;
require('./code.js');

function dependencyLookup (file) {
  switch (file) {
    case 'fs': return { readFileSync: function () { return "test contents"; } };
    default: return origRequire(file);
  }
}

If you now run node test.js, it will print out "test contents", even though it includes code.js.

Ruben Verborgh
  • 3,545
  • 2
  • 31
  • 43
  • Does this still work for you? All my relative paths break when I try this approach. `Error: Cannot find module './lib/model-store.js'` It's as if all modules are trying to load from the base directory of the tests, instead of the file under test. – Stefan Kendall Feb 21 '12 at 01:08
  • Since this code uses the regular require eventually, I suspect it is a path error on your side. Could you verify if it still works without the injector code? – Ruben Verborgh Mar 06 '12 at 09:01
2

I've also written a module to accomplish this, it's called rewire. Just use npm install rewire and then:

var rewire = require("rewire"),
    myModule = rewire("./path/to/myModule.js"); // exactly like require()

// Your module will now export a special setter and getter for private variables.
myModule.__set__("myPrivateVar", 123);
myModule.__get__("myPrivateVar"); // = 123


// This allows you to mock almost everything within the module e.g. the fs-module.
// Just pass the variable name as first parameter and your mock as second.
myModule.__set__("fs", {
    readFile: function (path, encoding, cb) {
        cb(null, "Success!");
    }
});
myModule.readSomethingFromFileSystem(function (err, data) {
    console.log(data); // = Success!
});

I've been inspired by Nathan MacInnes's injectr but used a different approach. I don't use vm to eval the test-module, in fact I use node's own require. This way your module behaves exactly like using require() (except your modifications). Also debugging is fully supported.

Community
  • 1
  • 1
Johannes Ewald
  • 17,665
  • 5
  • 44
  • 39
  • Interesting approach. I guess my implementation at https://github.com/lukeschafer/modulemock is more like injectr – Luke Schafer Jul 03 '12 at 02:06
  • while i find mocking require a common pattern in JS i still struggle with the idea of declaring dependencies with requiring a file vs declaring them in the constructor and resolving them at composition root. i've opened a SO question on this exact topic here: http://stackoverflow.com/questions/37836813/javascript-dependency-injection-dip-in-node-require-vs-constructor-injection – danfromisrael Jun 16 '16 at 14:14