4

I'm in the process of learning Node.js and am wondering about how people mock dependencies in their modules when unit testing.

For example: I have a module that abstracts my MongoDB calls. A module that uses this module may start out something like this.

var myMongo = require("MyMongoModule");
// insert rest of the module here.

I want to ensure I test such a module in isolation while also ensuring that my tests don't insert records/documents into Mongo.

Is there a module/package that I can use that proxies require() so I can inject in my own mocks? How do other's typically address this issue?

JamesEggers
  • 12,885
  • 14
  • 59
  • 86

4 Answers4

1

You can use a dependency injection library like nCore

To be honest, the hard part of this is actually mocking out the mongoDB API, which is complex and non trivial. I estimate it would take about a week to mock out most of the mongo API I use so I just test againts the a local mongodb database on my machine (which is always in a weird state)

Then with nCore specific syntax

// myModule.js
module.exports = {
  myMethod: function () { 
    this.mongo.doStuff(...)
  },
  expose: ["myMethod"]
};

// test-myModule.js
var module = require("myModule")

module.mongo = mongoMock
assert(module.myMethod() === ...)
Raynos
  • 166,823
  • 56
  • 351
  • 396
  • I agree that mocking Mongo's APIs is too extensive. I'm using it as an example since some people tend to apply a proxy or facade around their data store in which I'm attempt to just mock that proxy module. I'll definitely look into nCore. I also stumbled across [Horaa][1] which looks interesting as well. [1]: https://github.com/arunoda/horaa – JamesEggers Mar 01 '12 at 22:14
  • @JamesEggers horaa is ugly as hell. All it does is `var os = require("os"); os.overWriteShit = function () { ... }` – Raynos Mar 01 '12 at 22:22
  • Ok, after looking at nCore and Horaa, I agree with you that Horaa is definitely ugly. I came across Sandboxed-Module which is ideal for what I'm looking for. I'm curious to know your thoughts on it as well though. – JamesEggers Mar 02 '12 at 19:23
0

You easily mock require by using "a": https://npmjs.org/package/a

e.g. need to mock require('./foo') in unit test:
var fakeFoo = {};
var expectRequire = require('a').expectRequire;
expectRequire('./foo).return(fakeFoo);

//in sut:
var foo = require('./foo); //returns fakeFoo

0

Overwriting require to inject your mocks is a possible solution. However, I concur in Raynos' opinion:

I personally find the methodology of overwriting require on a file by file basis an "ugly hack" and prefer to go for proper DI. It is however optimum for mocking one or two modules on an existing code base without rewriting code for DI support.

To use proper dependency injection not only saves you an "ugly hack" but also allows you to apply additional use cases apart from injecting mocks. In production you may e.g. usually instantiate connections over http and in certain circumstances inject a different implementation to establish a connection over VPN.

If you want to look for a dependency injection container read this excellent article and check out Fire Up! which I implemented.

analog-nico
  • 2,750
  • 17
  • 25
0

After reviewing Ryanos's suggestion as well as the Horaa package on npm, I discovered this thread on the Google Group that pointed me towards Sandboxed-Module.

Sandboxed-Module allows me to inject/override require() without me having to expose such dependencies for my unit tests.

I'm still up for other suggestions; however, Sandboxed-Module appears to fit my needs at the moment.

JamesEggers
  • 12,885
  • 14
  • 59
  • 86
  • 1
    I personally find the methodology of overwriting require on a file by file basis an "ugly hack" and prefer to go for proper DI. It is however optimum for mocking one or two modules on an existing code base without rewriting code for DI support. – Raynos Mar 02 '12 at 19:25
  • @Raynos Node's module system (as typically used from what I've seen) doesn't really cater toward the idea of constructor injection is the tough part. While a module could be a simple namespace for classes to instantiate and inject into, I haven't seen that pattern a lot yet in Node code samples. Property injection of dependencies opens up a module a lot for patching in unintended ways which could turn into issues like what Rails had a year or so ago when they upgraded. – JamesEggers Mar 02 '12 at 20:02
  • General design feedback on nCore would be appreciated, I don't know what the optimum solution is for this but I think intercepting `require` is not – Raynos Mar 02 '12 at 20:08