How to sinon spy module export utility functions - javascript

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

Related

Spying on a function used as a constructutor

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

Node.js how should I unit test a function calling other functions

I want to unit test a module I've built.
To give an impression of what it looks like pretty much..
MyModule:
function MyModule(...) {
var self = this;
MyModule.init.call(self, ...);
}
util.inherits(MyModule, MySuperModule);
MyModule.init = function(...) {
...
};
MyModule.prototype.logic = function(..., done) {
calls fnA, fnB, fnC, fnD conditionally
done(...)
};
MyModule.prototype.fnA = function(...) {
...
};
MyModule.prototype.fnB = function(...) {
...
};
MyModule.prototype.fnC = function(...) {
...
};
MyModule.prototype.fnD = function(...) {
...
};
MySuperModule:
function MySuperModule(...) {
...
}
MySuperModule.prototype,fn = function(..., done) {
var self = this;
...
self.logic(..., function done(...) {
...
done(...)
});
}
Now MyModule.logic() is never called explicitly by a user, it is only invoked MySuperModule.fn().
Same goes for all other MyModule functions which are called conditionally based on the given parameters being passed through the delegating chain.
My questions are as follow:
Do I need to test all MyModule functions separately or just test MySuperModule.fn() with different parameters covering all possible scenarios
I know I need to test function in isolation (which if I do my previous question is wrong to ask because than I won't really have tested MyModule functions at all), how would I do that with the MySuperModule.fn(), because its done() callback is being called with arguments dependent on what the MyModule.logic() done() callback was called with, which is again, dependent on the arguments supplied to MySuperModule.fn() arguments.
It really depends how you're injecting MyModule on MySuperModule. But first of all I would point out that in unit tests you have to test MyModule separately and MySuperModule with a Mocked version from MyModule and all other dependencies. This is because you don't want to test MyModule twice, no need for that.
So to create stubs there is a library called Sinon.JS which works really fine.
So if for any reason you just want to make a spy to MyModule, which means you are just attaching a listener to MyModule (it is applied to MyModule methods) which counts and tells if a given method is ever called and how.
var MyModuleMethodASpy = sinon.spy(MyModulem 'MethodA');
MySuperModule.someMethod();
assert(MyModuleMethodASpy.called)
So this code creates a spy, triggers some method on MySuperModule and checks if MyModule.MethodA() is ever called.
You can create stubs as well if you want to control what dependencies return on specific methods eg :
var MyModuleStub = sinon.stub(MyModule, 'MethodA').returns([...somedata]);
In my view you should certainly be testing the individual functions, regardless of whether or not they're called directly by a user.
The purpose of unit testing is to try to ensure that the individual units of your test do what they're expected to do. If you're (relatively) sure that your individual functions/units behave as expected, you can have more confidence that they'll work nicely with each other.
It's hard to really glean from your code snippets the nature of your module, so suggesting how to implement your tests is difficult. However, it seems like what you're asking is how to verify whether your done/callback function is called and with which arguments.
For that I would recommend using a stub. I usually use sinon but I'm sure other similar tools are available.
var sinon = require( "sinon" );
var should = require( "chai" ).should();
var yourModule = require( "your-module" );
var doneStub = sinon.stub();
yourModule.yourFunction( ..., doneStub );
doneStub.should.have.been.called;
var args = doneStub.getCall( 0 ).args;
args[ 0 ].should.be.eql( ... );
// etc etc
You should also consider using a test runner, I like mocha!
You should do progressive testing. You should test each and every function.
Here how can you proceed.
Write test case for parent function. Mock the inner function where it is calling. You can use sinon library for mocking.
For second question, you can use sinon mock's yield functionality to mock any callback function and you can specify also which output you want from that callback. In this way you can test your function for multiple custom output with different scenario.

How to spy on a method inside a dependent module using jasmine-node

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()
})
})

How do I test a function which returns a function using Mocha, Sinon, and Chai?

I have a function which returns another function of the same name. Inside the second function, functions from other modules are being used. I just want to test whether the functions from other modules are being called.
Here is some code to clarify what I mean:
exports.getCache = function (model) {
return function getCache (req, res){
//some code
key = utils.uniqueKey(model, id)
//some code
res.json(result);
}
}
I want to check if uniqueKey is being called and if res.json is being called.
Any help at all is appreciated, thanks!
You can simply write:
sinon.spy(utils, 'uniqueKey')
in your test file, probably inside beforeEach function. Then it should be easy to check with chai.expect function whenever it was called:
expect(utils.uniqueKey.called).to.be.true();
expect(utils.uniqueKey.calledWith({modelValue: 'value'}, 'someId')).to.be.true();
where {modelValue: 'value'} and 'someId' are the actual values of model and id variables.
To make your component testable you have to make its dependencies injectable. As I see your code depends on utils. To properly mock this you can't just require('utils'), you have to implement some way to wire up dependencies. Using some DI library, or just extend public interface of your component:
var utils;
module.exports.dependencies = function (_utils) {
utils = _utils;
};
module.exports.getCache = function (model) {
key = utils.uniqueKey(model, id);
};
Then in productive code before using bootstrap your component calling component.dependencies(require('utils')). In test case you can pass there a spy: component.dependencies(spy);

Stub out module function

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!!");
});

Categories

Resources