Can I spy with sinon a function call in an external module function? - node.js

I have a module:
let xmlParser = require('./myTools').xmlParser;
function extractDataAndWrite(xmldata) {
let doc = xmlParser(xmldata);
...
}
module.exports = {
extractDataAndWrite,
};
now I want test the call of xmlParser in extractDataAndWrite:
var extractDataAndWrite = require('../services/importData.js').extractDataAndWrite;
var mytools = require('./myTools');
var sinon = require('sinon');
describe('Test extractDataAndWrite', function() {
it('call xmlParser', function(done) {
var xmlParserSpy = sinon.spy(mytools, 'xmlParser');
extractDataAndWrite("someXML");
console.log('xmlParserSpy: ' + xmlParserSpy.callCount);
done();
});
});
I expect get xmlParserSpy.callCount == 1 but it is 0!
My spy is not working, what must I change?

The problem is that when you create a spy on a function, you are replacing that function reference with a new function. That means that people that reference the old function will not use the new function. In your case, things are not working, as you wrap the exported function reference in mytools' after your own module has already referenced the old function reference.
The general techniques you need to look into are called dependency injection and link seams. The Sinon docs has a good tutorial on the latter, using proxyquire.
Essentially you would have this:
const proxyquire = require('proxyquire');
const toolsStub = createToolsStub();
const importData = proxyquire('../services/importData.js', {
'./myTools': toolsStub
});
function createToolsStub(){
return { xmlParser : sinon.stub().returns({mydoc:{foo:'bar'}};
}
Then later on in your test you could check the xmlParser for calls
assert(toolsStub.xmlParser.calledWith('arg1', 'arg2');

Related

Assert that the function passed into a stubbed function is the correct function

I'm trying to test my Node module using Mocha.
The module is very small here is an example...
import { sharedFunctionA, sharedFunctionB, commonFunction } from <file>
const functionA = token => _data => sharedFunctionA(token);
const functionB = () => data => sharedFunctionB(data);
exports.doThingA = token => {
commonFunction(functionA(token));
};
exports.doThingB = () => {
commonFunction(functionB());
};
This is only a brief example but it shows what I'm trying to do.
I need to test that doThingA and doThingB pass in the correct function to the commonFunction.
I have stubbed the commonFunction and I can see that it is being called but I can't assert that the function passed in is correct.
TBH... I'm beginning to think of restructuring this entirely to pass in some sort of enum to the commonFunction and running the respective function from there.
In this case you can stub on the sharedFunctionA and sharedFunctionB and then retrieve the argument of your stub on the commonFunction and call it. Then check your other stubs are being called with the desired arguments.
I know it's tedious but it is the only way I can think of with your code.
Quick example:
const assert = require('assert')
const sinon = require('sinon')
const sharedFunctions = require('<fileWithSharedFunctions>')
const commonStub = sinon.stub(sharedFunctions, 'commonFunction')
const sharedBStub = sinon.stub(sharedFunctions, 'sharedFunctionB')
const fileToTest = require('<fileToTest>')
fileToTest.doThingB()
commonStub.getCall(0).args[0]()
assert(sharedBStub.calledOnce)

How can I stub a constructor in node.js using sinon in a Cloud Functions for Firebase project?

I'm using GeoFire in a Cloud Functions for Firebase project I want to unit test.
In my original code GeoFire is used like this:
GeoFire = request('geofire');
...
var catGeoFire = new GeoFire(catGeofireRef);
return catGeoFire.set(storeId, [lat, lon]).then( () => {
console.log("Added store " + storeId + " to GeoFire" );
return Promise.resolve();
});
I need to stub both the call to the GeoFire constructor and the GeoFire().set() method.
I tried:
const geofireStub = sinon.stub(GeoFire, 'set').resolves();
But I received the error:
TypeError: Cannot stub non-existent own property set
I also tried:
const setStub = sinon.stub().resolves();
const geofireStub = sinon.stub(GeoFire).returns({set: setStub});
And I receive the error:
TypeError: Cannot stub non-existent own property set
Both errors happen at the geofireStub line.
Reading the sinon documentation I understood that I can stub an object's methods. However in this case GeoFire isn't an object; it is a constructor function. So I don't really know how can I stub it without having an associated object.
Thanks!
You need something like this, using rewire:
// target.js
var GeoFire = require('geofire');
var catGeoFire = new GeoFire(catGeofireRef);
return catGeoFire.set(storeId, [lat, lon]).then(() => {
console.log("Added store " + storeId + " to GeoFire" );
return Promise.resolve();
});
// test.js
var GeoFire = require('geofire');
var rewire = require('rewire')
var target = rewire('./target')
describe('target', () => {
it('test case', () => {
// arrange
// configure instance
var geoFireStub = sinon.createStubInstance(GeoFire)
geoFireStub.set.resolves()
// configure constuctor
var GeoFireMock = sinon.stub().returns(geoFireStub)
// 'GeoFire' is a mocked variable here
var revert = rewire('GeoFire', GeoFireMock)
// act (call tested module)
target()
// assert (should is just for example)
should(GeoFireMock).calledWithNew(/* params*/)
should(geoFireStub.set).calledWith(/* params*/)
//cleanup (rewire and stubs, prefer to use sandbox)
revert();
...
})
})
GeoFire is the constructor, but set is an instance method.
You should stub GeoFire.prototype I believe.
sinon.stub(GeoFire.prototype, 'set').resolves();

nodeJS proxyquire overwrite specific function of a require

I am currently testing a module in isolation using proxquire to overwrite a require of this module.
Overwriting a path of a require works fine with proxyquire. For example:
var bar = require('./bar');
But can you use proxyquire also to overwrite just a specific function of a module which is required in the module to test? So something like:
var bar = require('./foo').bar();
I need to stay at proxyquire for this since I am using it for mocking a http-request happening in another layer of the architecture. But in case of the test I need to mock the time for "now" in the module as well.
So currently I have this:
var uraStub = sendMockRequest(paramListOfCheckin, queryList);
var setNowStub = function(){ return 1425998221000; };
var checkin = proxyquire('../src/logic/logicHandlerModules/checkin', {
'../../persistence/ura' : uraStub,
'./checkin.setNow' : setNowStub
});
checkin.checkin(...)
The implementation of setNow is:
var setNow = function(){
return new Date().getTime();
};
var checkin = function (...) {
var now = require('./checkin').setNow();
Obviousley './checkin.setNow' : setNowStub in proxyquire doesn't work, since this is the wrong path. But using './checkin'.setNow() : setNowStub also doesn't work because of wrong syntaxis in the object-definition.
Any suggestions?
Thanks in advance!
What you are looking for is the noCallThru() and callThru() methods. https://github.com/thlorenz/proxyquire#preventing-call-thru-to-original-dependency
By default proxyRequire will call through to the mocked dependency which will allow you to pick the methods that you want to overwrite with your own custom function.
So if a dependency in the path '../foo' has a method bar() and fooBar() you would be able to mock out just bar by doing.
proxyquire.callThru();
var fooFunc = proxyquire('../foo', {
bar: () => return 'bar'
})
Now bar() will hit your custom overwritten funtion while fooBar() will be called as normal.

How to stub out express after you require it with jasmine?

I'm trying to get the code below under test when occurred to me that I already included express at the top of this file. Can you some how monkey patch the express object after it's already loaded?
var express = require('express')
Helper = (function() {
var HelperObject = function(params) {
this.directories = params.directories;
};
HelperObject.prototype.addStaticPath = function(app) {
for(i = 0; i < this.directories.length; i++) {
var static = express.static('/public');
app.use(static);
}
};
return HelperObject;
})();
The problem is that when you create a node module the required modul is bound in the closure of the module and you can't start spying on it cause it isn't visible in your test.
There is Gently where you can override require but it will sprinkle your code with boilerplate test related code.
From the docs:
Returns a new require functions that catches a reference to all
required modules into gently.hijacked.
To use this function, include a line like this in your 'my-module.js'.
if (global.GENTLY) require = GENTLY.hijack(require);
var sys = require('sys');
exports.hello = function() {
sys.log('world');
};
Now you can write a test for the module above:
var gently = global.GENTLY = new (require('gently'))
, myModule = require('./my-module');
gently.expect(gently.hijacked.sys, 'log', function(str) {
assert.equal(str, 'world');
});
myModule.hello();

How should I pass options to a node module?

If I have a node module (I wrote) and I want to pass it a value, I could do this:
var someValue process.env.SomeKey || '';
var someModule = require('./someModule');
someModule.setOption({ 'SomeKey' : someValue });
but it feels like I am reinventing the wheel.
Is there a better way to do this or is it totally subjective?
In general, you simply export a function from the module:
module.exports = function(opts){
return {
// module instance
};
}
then in the requiring page:
var mod = require('module')({ someOpt: 'val' });
But in reality, do it however you want. There's no set-in-stone standard.
I generally build modules that have similar components, sometimes just one class, or even just a selections of methods.
(function () {
var myClass = function (opts) {
this.opts = opts;
};
myClass.prototype.blah = function () {
console.log('blah');
};
exports.myClass = myClass;
})();
Then in your file that is using that module.
var mymodule = require('./mymodule');
var myInstance = new mymodule.myClass({opt1: 'blah'});
myInstance.blah();
Of course you don't need to just pass around an object of options :)
Yes, it is totally subjective.
Doing it the way you demonstrated is fine. You can also just export a function or a class by assigning it to module.exports.

Resources