How do other node developers who use sinon mock out constructor calls within their unit tests? For example, suppose I have some function foo
function foo() {
var dependency = new Dependency(args);
// do stuff with dependency
}
exports.module.foo = foo;
and in a separate test file I have some test in which I want to verify what the Dependency constructor is called with (args), and I need to control what it returns
it('should call Dependency constructor with bar', function() {
var foo = require('myModule').foo
var DependencyMock; //code to make the mock
foo();
expect(DependencyMock.calledWith(bar)).to.equal(true);
});
The problem is that sinon can only mock functions attached to an object, so we have to attach the constructor to an object before it can be mocked.
What I've been doing is just making an object to attach the constructor to in the module making the constructor call, calling the constructor as a method of that object, then exporting the object to use it in tests:
var Dependency = require('path/to/dependency');
var namespace = {
Dependency: Dependency
}
function foo() {
var dependency = new namespace.Dependency(args);
// do stuff with dependency
}
exports.moduole.foo = foo;
exports.module.namespace = namespace;
testfile:
it('should call Dependency constructor with bar', function() {
var foo = require('myModule').foo;
var namespace = require('myModule').namespace;
var DependencyMock = sinon.mock(namespace, 'Dependency').returns(0);
foo();
expect(DependencyMock.calledWith(bar)).to.equal(true);
});
This works, but it feels really clunky to expose an object on my module just for the sake of testing it.
Any tips?
In order to mock a constructor function, the module factory must return a constructor function. In other words, the module factory must be a function that returns a function - a higher-order function (HOF). Since calls to jest. mock() are hoisted to the top of the file, Jest prevents access to out-of-scope variables.
In Jest, Node. js modules are automatically mocked in your tests when you place the mock files in a __mocks__ folder that's next to the node_modules folder. For example, if you a file called __mock__/fs. js , then every time the fs module is called in your test, Jest will automatically use the mocks.
The constructor method is a special method of a class for creating and initializing an object instance of that class. Note: This page introduces the constructor syntax. For the constructor property present on all objects, see Object. prototype.
Mock functions allow you to test the links between code by erasing the actual implementation of a function, capturing calls to the function (and the parameters passed in those calls), capturing instances of constructor functions when instantiated with new , and allowing test-time configuration of return values.
I think it's worth asking why you'd want to mock a constructor of a dependency instead of injecting that dependency?
Consider your example code:
// in "foo.js"
function foo() {
var dependency = new Dependency(args);
// do stuff with dependency
}
exports.module.foo = foo;
If Dependency
is required for foo
to work you can inject it as an argument of foo
:
// in "foo.js"
function foo(dependency) {
// do stuff with dependency
}
exports.module.foo = foo;
// in "bar.js"
var foo = require('./foo.js')(new Dependency(args));
With this change it's now trivial to inject any Test Double in your tests (to find out more about JavaScript Test Doubles have a look at my article on the subject).
This approach makes the dependencies of your function/module explicit, but requires you to wire them up at some point (here: require('./foo.js')(new Dependency(args));
).
If you didn't want to wire things up manually there's another approach you can take using rewire and replacing constructor with factory method:
// in "dependency.js"
module.exports= function(args) {
return new Dependency(args);
}
// in "foo.js"
var dependency = require('./dependency');
function foo() {
var dep = dependency(args);
// do stuff with dependency
}
exports.module.foo = foo;
and in your test:
var rewire = require("rewire"),
foo = rewire("../lib/foo.js");
it('should call dependency... ', function() {
foo.__set__("dependency", /* some spy */ );
foo();
});
Hope this helps!
Jan
I used a workaround for this:
// Keep a reference to your dependancy class.
const dependencyClass = Dependency;
let item = new Dependency();
// Replace class with a stub.(optionally return an item.
Dependency = sinon.stub(Dependency, 'constructor').returns(item);
// Call your code that you expect the class constructor to be called.
foo();
assert.isTrue(Dependency.calledWithNew());
assert.equal(1, Dependency.callCount);
// Restore class reference.
Dependency = dependencyClass;
Additionally, in the above case, an item is returned, so the user can have access to the dependency for further testing. eg.
assert.equal(item.someValue, 10);
You can have other problems using this, eg defined properties will no longer be available for the class. I agree with Jan Molek, use this in case you cannot change the code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With