How to spyOn NodeJS module singleton with Jasmine - node.js

I want to use Jasmine to spyOn a module which exports a method directly:
myModule
module.exports = (arg, arg) => {
//do something...
callAMethod();
//return
}
Jasmine
spyOn(myModule);
// since I don't have any method to use spyOn(myModule, <method>)
I found I could use this:
//describe..
var myModule = require('./myModule');
it...
myModule = jasmine.createSpy().and.callFake(function() {
return false;
}); // <-this should work
functionBeingTested(..args);
expect(myModule).toHaveBeenCalled(); /*I get false, even if
it is actually called
in the functionBeingTested*/
Another solution i found was to spyOn myModule.prototype or set jasmine.createSpy().and.returnValue(false) but I have no success with both either.
How can I use spyOn like I said above?

I've created a simple snippet which mimics the require js
functionality to load the library and call the method.
I've listed three spies each with a different scenario explaining how
to spy
Note:
I've not used the require.js in itself but mimicked its functionality
however
Because the require.js exports the module.exports, we have no
control over the functionality of the require call and hence we might
not be able to install a spy on that method.
Please follow the each it case to for the description.
Hope this helps!
var callAMethod = function() {
return "Hello";
}
var myModule = {};
myModule.exports = function(name, title) {
return callAMethod() + " " + title + "." + name;
}
// Note that this require mimics the actual require.js require methodology, bascially returns the instantiated version of the library to be used.
var require = function() {
return myModule.exports;
}
// some function that uses the require library to load the js and then does something with it.
var testFunc = function() {
var myMod = require('abc');
return myMod("Bruce", "Mr");
}
describe('test mymodule', function() {
// This test fails because though you are mocking the instance here and not the actual call.
it('test that fails', function() {
var myModuleTest = require('abc');
myModuleTest = jasmine.createSpy().and.callFake(function() {
return false;
});
var result = testFunc();
console.log(result);
expect(result).toBe(false);
});
// this test passes since we have hijacked the require attribute itself and then routed its call to return the Boolean true which means that myModule now return the true statement
it('test that passes', function() {
require = jasmine.createSpy().and.callFake(function() {
return function() {
return true;
};
});
var result = testFunc();
console.log(result);
expect(result).toBe(true);
});
//callAMethod method call from module.exports may not tested since its the require js that does this call and you have no hook into it, however you can directly test the method like this
it('test callAMethod', function() {
console.log(myModule.exports("Bruce", "Mr"));
spyOn(myModule, 'exports').and.returnValue("Hello Mr.Wayne!");
var testVal = myModule.exports("Bruce", "Mr");
expect(testVal).toEqual("Hello Mr.Wayne!");
})
});

Related

Sinon stubs not being used

I have a lambda function which is referencing a module define in another file.
Instead of having to get all the scenarios right in my tests I'd like to just have a stub of that module.
In app.js
var Service = require("./Services/Service");
let service = new Service(event.Environment);
var regulatorResults = await service.GetModifiers(budgeted, irrelevant, variables);
Service is defined as follows:
function Service(environment) {
async function GetModifiers(ids, irrelevant, things) {
//logic happens
}
return { GetModifiers };
}
module.exports = Service;
I've tried many different iterations in my tests but where I feel closest is with this:
var assert = require('assert');
var sinon = require('sinon');
var service = require('../Services/Service');
describe('Calculator', function () {
before(function () {
var service = sinon.createStubInstance(regulatorService);
sinon.stub(service.prototype, "GetModifiers").callsFake(async function(IDs, thing, thing) {
return IDs.map(id=> new { ID: id, Modifier: 1 });
});
});
When I run the actual tests, the results of that service are [], even if I code the stub to return a set value, and if I throw in a console.log, it doesn't get hit ever.
The tests fail with error Error: Trying to stub property 'GetModifiers' of undefined.
Thanks for your help!

How to stub/spy external methods in a module that are called internally?

//entity module
function isUnique(name) {
return true;
}
exports.isUnique = isUnique;
function create(name) {
if (isUnique(name)) { return console.log(name); }
else { return new Error(); }
}
exports.create = create;
Is it possible to stub the "isUnique" method to change the behavior of the "create" method?
var entity = require("./entity");
var sinon = require("sinon");
var assert = require("assert");
var stub = sinon.stub(entity, "isUnique", function (name) { return false });
entity.create("test");
stub.restore();
assert(stub.calledOnce, true);
In short: no.
Your module holds two references to the isUnique function: one from the function declaration itself, and one assigned to exports.isUnique.
The create function is using the first, but Sinon is stubbing the second.
For the stub to get called, create needs to use the reference that gets stubbed:
if (exports.isUnique(name)) { return console.log(name); }
// `this.isUnique` also works, because `this` === `module.exports`
Alternatively, you can use a module like rewire.

How can I mock a function/property of a node module exported as a function using sinon?

I have a service module that is exported as a function. I need to pass a couple of things into it, like a configuration object so it does need to retain this structure. I am trying to stub out a function from the service but can't figure it out. In my app, I have a function that makes an API call that is problematic during testing so I'd like to stub it. (I understand I'd have to write my test differently to handle the async issue)
// myService.js
module.exports = function(config) {
function foo() {
returns 'bar';
}
return {
foo: foo
};
};
// test.js
var config = require('../../config');
var request = require('supertest');
var chai = require('chai');
var expect = chai.expect;
var sinon = require('sinon');
var myService = require('./myService.js')(config);
describe('Simple test', function(done) {
it('should expect "something else", function(done) {
var stub = sinon.stub(myService, 'foo').returns('something else');
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
stub.restore();
})
.end(done);
});
});
* /testRoute I set up as a simple GET route that simply returns the value from myService.foo()
The above is not working, and I believe it has to do with the way my service is exporting. If I write the service as below, the stub works fine.
module.exports = {
test: function() {
return 'something';
}
};
But again, I need to be able to pass in information to the module so I would like to keep my modules in the original structure above. Is there a way to stub a function from a module that exports in that manner? I was also looking into proxyquire but not sure if that is the answer.
The reason why your test stub does not work is that the foo function is created every time the module initializer is called. As you discovered, when you have a static method on the module, then you are able to stub.
There are a variety of solutions to this problem-- but the simplest is to expose the method statically.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function foo() {
return 'bar'
}
It's ugly, but works.
What if the foo function has a closure to variables within the service (which is why it lives within the service initializer). Then unfortunately these need to be explicitly passed in.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function(config) {
return function foo() {
return config.bar;
}
}
Now you can safely stub the module.
However, how you are stubbing should be considered unsafe. Only if your test works perfectly does the stub get cleaned up. You should always stub within the before and after (or beforeEach and afterEach) fixtures, such as:
// We are not configuring the module, so the call with config is not needed
var myService = require('./myService.js');
describe('Simple test', function(done) {
beforeEach(function () {
// First example, above
this.myStub = sinon.stub(myService, foo).returns('something else');
// Second example, above
this.myStub = sinon.stub(myService, foo).returns(function () {
returns 'something else';
});
});
afterEach(function () {
this.myStub.restore();
});
it('should expect "something else", function(done) {
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
})
.end(done);
});
});
There are other options to be able to stub dependencies using dependency injection. I recommend you look at https://github.com/vkarpov15/wagner-core or my own https://github.com/CaptEmulation/service-builder

Asynchronous nodejs module exports

I was wondering what the best approach is for configuring a module export. "async.function" in the example below could be a FS or HTTP request, simplified for the sake of the example:
Here's example code (asynmodule.js):
var foo = "bar"
async.function(function(response) {
foo = "foobar";
// module.exports = foo; // having the export here breaks the app: foo is always undefined.
});
// having the export here results in working code, but without the variable being set.
module.exports = foo;
How can I export the module only once the async callback has been executed?
edit
a quick note on my actual use-case: I'm writing a module to configure nconf (https://github.com/flatiron/nconf) in an fs.exists() callback (i.e. it will parse a config file and set up nconf).
Your export can't work because it is outside the function while the foodeclaration is inside. But if you put the export inside, when you use your module you can't be sure the export was defined.
The best way to work with an ansync system is to use callback. You need to export a callback assignation method to get the callback, and call it on the async execution.
Example:
var foo, callback;
async.function(function(response) {
foo = "foobar";
if( typeof callback == 'function' ){
callback(foo);
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback = cb;
}
}
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
Multiple callback way
If your module need to be called more than once you need to manage an array of callback:
var foo, callbackList = [];
async.function(function(response) {
foo = "foobar";
// You can use all other form of array walk.
for(var i = 0; i < callbackList.length; i++){
callbackList[i](foo)
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback.push(cb);
}
}
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
Promise way
You can also use Promise to solve that. This method support multiple call by the design of the Promise:
var foo, callback;
module.exports = new Promise(function(resolve, reject){
async.function(function(response) {
foo = "foobar"
resolve(foo);
});
});
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js').then(function(foo){
//Here code using foo;
});
See Promise documentation
An ES7 approach would be an immediatly invoked async function in module.exports :
module.exports = (async function(){
//some async initiallizers
//e.g. await the db module that has the same structure like this
var db = await require("./db");
var foo = "bar";
//resolve the export promise
return {
foo
};
})()
This can be required with await later:
(async function(){
var foo = await require("./theuppercode");
console.log(foo);
})();
ES6 answer using promises:
const asyncFunc = () => {
return new Promise((resolve, reject) => {
// Where someAsyncFunction takes a callback, i.e. api call
someAsyncFunction(data => {
resolve(data)
})
})
}
export default asyncFunc
...
import asyncFunc from './asyncFunc'
asyncFunc().then(data => { console.log(data) })
Or you could return the Promise itself directly:
const p = new Promise(...)
export default p
...
import p from './asyncModule'
p.then(...)
Another approach would be wrapping the variable inside an object.
var Wrapper = function(){
this.foo = "bar";
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
});
}
module.exports = new Wrapper();
If the initializer has error, at least you still get the uninitialized value instead of hanging callback.
You can also make use of Promises:
some-async-module.js
module.exports = new Promise((resolve, reject) => {
setTimeout(resolve.bind(null, 'someValueToBeReturned'), 2000);
});
main.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// outputs 'someValueToBeReturned' after 2 seconds
The same can happen in a different module and will also resolve as expected:
in-some-other-module.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// also outputs 'someValueToBeReturned' after 2 seconds
Note that the promise object is created once then it's cached by node. Each require('./some-async-module') will return the same object instance (promise instance in this case).
Other answers seemed to be partial answers and didn't work for me. This seems to be somewhat complete:
some-module.js
var Wrapper = function(){
this.callbacks = [];
this.foo = null;
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
this.callbacks.forEach(function(callback){
callback(null, wrapper.foo);
});
});
}
Wrapper.prototype.get = function(cb) {
if(typeof cb !== 'function') {
return this.connection; // this could be null so probably just throw
}
if(this.foo) {
return cb(null, this.foo);
}
this.callbacks.push(cb);
}
module.exports = new Wrapper();
main.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined
});
main2.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined in another script
});

Sinon.js and testing with events and new

i'm trying to mock a node.js application, but it doesn't work as expected.
I have a node.js Module called GpioPlugin with following method:
function listenEvents(eventId, opts) {
if(!opts.pin) {
throw new Error("option 'pin' is missing");
}
var listenPort = new onOff(opts.pin, 'in', 'both', {persistentWatch: true});
listenPort.watch(function(err, value) {
process.emit(eventId+'', value);
});
}
if(typeof exports !== 'undefined') {
exports.listenEvents = listenEvents;
}
and now i want to write a test using sinon for this method, but i don't know how... What would be the best way to test this?
This tree parts would be fine, if they get tested:
Error (no problem)
generation of onOff (how?)
event with correct params
If it's not already, you're going to want to put onOff into a module so that your test can inject a stub in.
var sinon = require("sinon");
var process = require("process");
var onOffModule = require(PATH_TO_ONOFF); //See note
var gpio = require(PATH_TO_GPIO);
var onOffStub;
var fakeListenPort;
beforeEach(function () {
//Stub the process.emit method
sinon.stub(process, "emit");
//Constructor for a mock object to be returned by calls to our stubbed onOff function
fakeListenPort = {
this.watch = function(callback) {
this.callback = callback; //Expose the callback passed into the watch function
};
};
//Create stub for our onOff;
onOffStub = sinon.stub(onOffModule, "onOff", function () {
return fakeListenPort;
});
});
//Omitted restoring the stubs after each test
describe('the GpioPlugin module', function () {
it('example test', function () {
gpio.listenEvents("evtId", OPTS);
assert(onOffStub.callCount === 1); //Make sure the stub was called
//You can check that it was called with proper arguments here
fakeListenPort.callback(null, "Value"); //Trigger the callback passed to listenPort.watch
assert(process.emit.calledWith("evtId", "Value")); //Check that process.emit was called with the right values
});
});
Note: The exact mechanics of replacing onOff with a stub may vary depending how you require it.
Things get a little more complicated if you require onOff directly, rather than requiring a module that includes onOff. In that case I think you might need to look into something like proxyquire.

Resources