Edit: Being a little bit more precise.
I want to test usecases for a Github API wrapper extension, that our team has created. For testing, we don't want to use API wrapper extension directly, so we want to stub out its functions. All calls to the API wrapper should be stubbed out for the tests, not just creating a clone stub.
I have a module "github" in Node.js:
module.exports = function(args, done) {
...
}
And I am requiring it like this:
var github = require('../services/github');
Now, I would like to stub out github(...) using Sinon.js:
var stub_github = sinon.stub(???, "github", function (args, callback) {
console.log("the github(...) call was stubbed out!!");
});
But sinon.stub(...) expects from me to pass an object and a method and does not allow me to stub out a module that is a function.
Any ideas?
There might be a way to accomplish this in pure Sinon, but I suspect it would be pretty hacky. However, proxyquire is a node library that is designed for solving these sort of issues.
Supposing you want to test some module foo that makes use of the github module; you'd write something like:
var proxyquire = require("proxyquire");
var foo = proxyquire(".foo", {"./github", myFakeGithubStub});
where myFakeGithubStub can be anything; a complete stub, or the actual implementation with a few tweaks, etc.
If, in the above example, myFakeGithubStub has a property "#global" set as true, (i.e. by executing myFakeGithubStub["#global"] = true) then the github module will be replaced with the stub not only in the foo module itself, but in any module that the foo module requires. However, as stated in the proxyquire documentation on the global option, generally speaking this feature is a sign of poorly designed unit tests and should be avoided.
I found that this worked for me...
const sinon = require( 'sinon' );
const moduleFunction = require( 'moduleFunction' );
// Required modules get added require.cache.
// The property name of the object containing the module in require.cache is
// the fully qualified path of the module e.g. '/Users/Bill/project/node_modules/moduleFunction/index.js'
// You can get the fully qualified path of a module from require.resolve
// The reference to the module itself is the exports property
const stubbedModule = sinon.stub( require.cache[ require.resolve( 'moduleFunction' ) ], 'exports', () => {
// this function will replace the module
return 'I\'m stubbed!';
});
// sidenote - stubbedModule.default references the original module...
You have to make sure that you stub the module (as above) before it's required elsewhere...
// elsewhere...
const moduleFunction = require( 'moduleFunction' );
moduleFunction(); // returns 'I'm stubbed!'
Simplest solution is to refactor your module:
instead of this:
module.exports = function(args, done) {
...
}
do this:
module.exports = function(){
return module.exports.github.apply(this, arguments);
};
module.exports.github = github;
function github(args, done) {
...
}
Now you can require it with:
const github = require('../services/github.js');
//or
const github = require('../services/github.js').github;
To stub:
const github = require('../services/github.js');
let githubStub = sinon.stub(github, 'github', function () {
...
});
If you are doing
var github = require('../services/github');
in global scope, then you can using 'global' as the object and 'github' as the method to be stubbed out.
var stub_github = sinon.stub(global, "github", function (args, callback) {
console.log("the github(...) call was stubbed out!!");
});
Related
In one of my unit tests I need to spy on a function which is used as a constructor by another function with Sinon library. As per their documentation
...sinon.spy(object, "method") creates a spy that wraps the existing function object.method. The spy will behave exactly like the original method (including when used as a constructor)...
But so far I have failed to make it work even when trying to spy on a constructor called within the test function let alone called by another function.
Unit test:
it('constructor was called.', () => {
const Foo = require('../app/foo');
const fooModule = module.children.find(m => m.id.includes('foo.js'));
const fooSpy = sinon.spy(fooModule, 'exports');
const f = new Foo(5);
expect(fooSpy).calledOnce;
});
Function to be instantiated:
const Foo = function(param) {
console.log('Foo called with: ' + param);
};
Foo.prototype.bar = function(x) {
console.log(`Foo.prototype.bar() called with x: ` + x);
};
module.exports = Foo;
When I step with debugger I can see the function at const fooSpy = sinon.spy(fooModule, 'exports'); being replaced by the spy (has all all sinon properties added like calledOnce and so on...) however when on new Foo(5); it appears that Foo is a non spy object.
I thought this might be a scoping or reference error but I can't seem to find where else Foo would be defined apart from within module.children. It is not on global neither its on window since its running on node.
Currently the test of course fails with:
Foo called with: 5
AssertionError: expected exports to have been called exactly once, but it was called 0 times
at Context.it (test/fooTest.js:18:23)
Thanks in advance for any help!
Your issue is not really with the sinon.spy API, but with how Node modules import functions. When calling sinon.spy, unless we are testing a callback function, we usually need an Object to be the context on which we want to spy on a particular method. This is why your example tries to access the exports Object of the foo.js module. I am not aware of Node giving us access to such an Object.
However, for your example to work, we don't need access to the Foo module's exports, we could simply create a context of our own. For example:
const chai = require("chai");
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
const expect = chai.expect;
chai.use(sinonChai);
describe("foo", function () {
it('constructor was called.', function () {
const context = {
Foo: require("../app/foo"),
};
const fooSpy = sinon.spy(context, "Foo");
new context.Foo(5);
expect(fooSpy).to.be.calledOnceWith(5);
});
});
Of course, the above test is a working solution to the problem provided in your example, but, as a test, it is not very useful because the assertion just verifies the line above it.
Spies are more useful when they are dependencies of the System Under Test (SUT). In other words, if we have some module that is supposed to construct a Foo, we want to make the Foo constructor a Spy so that it may report to our test that the module did indeed call it.
For example, let's say we have fooFactory.js module:
const Foo = require("./foo");
module.exports = {
createFoo(num) {
return new Foo(num);
},
};
Now we would like to create a unit-test that confirms that calling the createFoo function of the fooFactory.js module calls the Foo constructor with the specified argument. We need to override fooFactory.js's Foo dependency with a Spy.
This returns us to our original problem of how can we turn an imported (constructor) Function into a spy when it is not a method on a context Object and so we cannot overwrite it with sinon.spy(context, 'method').
Fortunately, we are not the first ones to encounter this problem. NPM modules exist that allow for overriding dependencies in required modules. Sinon.js provides a How-To on doing this sort of thing and they use a module called proxyquire.
proxyquire will allow us to import the fooFactory.js module into our unit-test, but also (and more importantly) to override the Foo that it depends on. This will allow our unit-test to make fooFactory.js use a sinon.spy in place of the Foo constructor.
The test file becomes:
const chai = require("chai");
const proxyquire = require("proxyquire");
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
const expect = chai.expect;
chai.use(sinonChai);
describe("fooFactory", function () {
it("calls Foo constructor", function () {
const fooSpy = sinon.spy();
const { createFoo } = proxyquire("../app/fooFactory", {
"./foo": fooSpy,
});
createFoo(5);
expect(fooSpy).to.be.calledOnceWith(5);
});
});
Here is a code snippet
body.user_id = userObj._id;
exports.inFCID(conn, obj.fcid, body, fcid, 0).then(function (r) {
exports.getUserById(conn, body.user_id).then(function (u) {
console.log("after getuserbyid", u);
Here I am sort of didn't understand why user have done something like
exports.inFCID(conn, obj.fcid, body, fcid, 0).then(function (r
of to be precise what does it do? I have previously encounter things like module.exports and export default statement ( export something statements) but this seems to be new.. Can someone explain me what this snippets would normally do? Ignoring what is inside those functions (inFCID) or what does export.something do in middle of the code..
It's expected that inFCID export is defined in this module as well:
exports.inFCID = function inFCID (...) {...};
When an export is defined as function expression, inFCID function is not available as inFCID but as exports.inFCID.
Referring exported functions as exports.inFCID inside module is a common recipe to improve testability in CommonJS modules; the same recipe won't work with ESM; a module needs to be separated when used with ES modules, as explained in this answer. Module exports can be spied or mocked outside the module:
const foo = require('foo');
...
spyOn(foo, 'inFCID');
foo.bar();
expect(foo.inFCID).toHaveBeenCalled();
This would be impossible if inFCID(...) was referred directly.
exports is a regular object.
If you have a code like this:
function test() {}
module.exports.test = test
Then nodejs will convert it into something like this:
function moduleInvocation(module, exports) {
function test() {}
module.exports.test = test
}
// a rough dummy code, to illustrat what node does on require
function require(moduleName) {
var module = { exports : {} };
//
// some code that loades `moduleName` and wraps it into `moduleInvocation`
//
moduleInvocation(module, module.exports)
return module;
}
So if someone writes exports.inFCID() then it is not different to:
var obj = {};
obj.inFCID = function() {}
obj.inFCID();
But it does not make any sense to write it that way except if you compose the exports of the module out of the content of some sub files.
I'm trying to unit test some express middleware which has dependencies on some classes I've made.
Middleware.js
const MyClass = require('../../lib/MyClass');
const myClassInstance = new MyClass();
function someMiddleware(req, res) {
myClassInstance.get().then(function(resp) {
res.render('somefile.html');
});
};
Test.js
const MyClass = require('../../lib/MyClass');
const sinon = require('sinon');
const chai = require('chai');
const expect = chai.expect;
// File we are testing
const someMiddleware = require('path/to/middleware');
MyClassMock = sinon.spy(function() {
return sinon.createStubInstance(MyClassMock);
});
describe('My Middleware', function() {
let renderSpy, classStub;
let res = {
render: function() {}
}
beforeEach(function() {
renderSpy = sinon.stub(res, 'render');
})
afterEach(function() {
renderSpy.restore();
})
it('should call render function', function() {
someMiddleware.someMiddleware(req, res);
expect(renderSpy.calledOnce);
});
});
I have tried a bunch of things however I can't quite seem to actually mock this class and mock the instance created! when it runs it will try to run the actual class with it's related dependencies inside.
Cheers folks!
Your Intent: replace/stub the dependencies of your middleware.
Your problem: this isn't possible from the outside of your module. You somehow need to "intercept" which code will be used.
There are two options, both of which I have written at length about in other places (1, 2, 3), so I'll do the shortform here:
Manual dependency injection
In your middleware module, provide a __setMyClassInstance(stubbedInstance) method that is only called from tests.
Pro: very easy, no frameworks needed
Con: test-only code that opens up your module
Link seams
This is about replacing require() calls in your code with another module loader that returns objects of your liking. In your test code:
const myMiddleware = proxyquire('../../src/middleware', { '../../lib/MyClass': myStubbedInstance })
Pro: Achieves the same as DI, but requires no code changes in the module
Con: not as clear cut what is going on, requires learning a new dependency
If you are stuck after looking at these very brief explanations, I suggest looking at my provided links for long-form explanations and links to tutorials on Link Seam libraries such as proxyquire and rewire.
In javascript (ES6), I have a utility module which just contains some functions, and then in the end of the file, I export them like so:
module.exports = {
someFunction1,
someFunction2,
someFunction3,
}
Then I want to write unit tests for those functions. Some of the functions depend on each other; they call each other in a way, that for example, someFunction1 might call someFunction2. There's no circular problems.
Everything works well, until I need to spy that one of the functions is called. How can I do it? Currently I'm using Chai and Sinon.
In the test file, I have imported the whole file as a module:
const wholeModule = require('path/to/the/js/file')
And finally, my test looks like following:
it('should call the someFunction2', (done) => {
const spy = sinon.spy(wholeModule, 'someFunction2')
wholeModule.someFunction1() // someFunction2 is called inside someFunction1
assert(spy.calledOnce, 'someFunction2 should be called once')
done()
})
The problem is, that the test fails, because in someFunction1, the someFunction2 function is used directly. I apply the spy to the module object's function. But that's a different object. Here's an example of the someFunction1:
function someFunction1() {
someFunction2()
return 2;
}
I know the reason why it won't work, but I don't know what would be the best practise in this case to make it work? Please help!
You can use rewire module. Here is an example:
Source code:
function someFunction1() {
console.log('someFunction1 called')
someFunction2();
}
function someFunction2() {
console.log('someFunction2 called')
}
module.exports = {
someFunction1: someFunction1,
someFunction2: someFunction2
}
Test case:
'use strict';
var expect = require('chai').expect;
var rewire = require('rewire');
var sinon = require('sinon');
var funcs = rewire('../lib/someFunctions');
it('should call the someFunction2', () => {
var someFunction2Stub = sinon.stub();
funcs.__set__({
someFunction2: someFunction2Stub,
});
someFunction2Stub.returns(null);
funcs.someFunction1();
expect(someFunction2Stub.calledOnce).to.equal(true);
});
As you are already aware this happens because you are stubbing the exported reference not the actual method in the module. It works in any module that includes the one you are replacing since they include the exported references but when inside the same module it's just calling the local function.
The easiest solution I've found is just to call the reference:
function someFunction1() {
this.someFunction2()
return 2;
}
I am trying to write jasmine tests for a module-(say moduleA) which 'requires' another module-(moduleB).
======> moduleB.js
function moduleBFunction(){
console.log('function inside moduleB is called');
}
======> moduleA.js
var moduleB = require('./moduleB');
function moduleAfunction(input){
if(input){
moduleB.moduleBFunction()
}
}
I want to write a jasmine test case that tests when i call moduleAfunction, is it calling moduleBfunction or not. I tried to write a test using spyOn(). but i am not sure how can i mock a method inside a dependent module. I did some research and found i might be able to use 'rewire' module for this purpose like below
var moduleB = require('../moduleB');
moduleB.__set__('moduleBfunction', moduleBfunctionSpy);
moduleA.__set__('moduleB', moduleB);
it('should call moduleBfunction', function(){
moduleA.moduleAfunction()
expect(moduleB.moduleBfunction()).toHaveBeenCalled()
});
but I feel there should be a simpler way.
Please suggest.
I recommend sinon.js
var sinon = require('sinon')
var moduleA = require('../moduleA')
var moduleB = require('../moduleB')
it('should call moduleBfunction', function() {
var stub = sinon.stub(moduleB, 'moduleBfunction').returns()
moduleA.moduleAfunction()
expect(moduleB.moduleBfunction.calledOnce)
stub.restore()
})
You can easily fake many different behaviours like:
stub throws
stub returns a certain value
stub yields (mimicking async callback)
stub works restricted with just for certain input arguments
Don't forget to restore each stub before executing the next test. It's best to use sandboxes and afterEach / beforeEach
describe('tests which require some fakes', function() {
var sandbox
beforeEach(function() {
sandbox = sinon.sandbox.create()
})
afterEach(function() {
sandbox.restore()
})
})