Stub internal calls on dependency of library with Sinon.js - javascript

I am writing a library which has an entry file that looks like this:
function MyLibrary(options){
this._options = {// defaults here};
this.setupOptions(options);
this._initInstance();
}
MyLibrary.prototype.randomMethod = function(){
}
MyLibrary.prototype._initInstance = function(){
this._loadImage();
this._internalInstance = new OtherThirdPartyDependency(this._options);
}
module.exports = MyLibrary;
In my tests, I would like to create a real instance of MyLibrary, but I want to create a stub of OtherThirdPartyDependency.
Here is my test file so far. How can I achieve this?
describe('My Library Module', () => {
let sandbox;
let myLibInstance;
beforeEach(() => {
sandbox = sinon.createSandbox({});
myLibInstance = new MyLibrary({option: 1, option: 2});
// FAILS HERE because initializing MyLibrary make a call OtherThirdPartyDependency constructor.
});
afterEach(() => {
sandbox.restore();
});
it('should call setOptions and _loadImage on init', () => {
expect(myLibInstance.setOptions).to.have.been.calledOnce;
expect(myLibInstance._loadImage).to.have.been.calledOnce;
})
});
There is a method in sinon createStubInstance, but I'm not sure how to apply in here because OtherThirdPartyDependency is not a method that I can directly stub on MyLibrary. How can I stub OtherThirdPartyDependency?

Related

Sinon Spy for Non-Class Methods

I have a javascript file with a bunch of util functions in a file called utils.js
export const processListOfItems = (input): [] => {
let listOfItems = [];
for (var index = 0; index < rawPayload.length; ++index) {
listOfItems.push(someFunction(item));
}
return listOfItems;
};
someFunction is defined in utils.js as well.
For the test, I would like to stub "someFunction," but am having trouble figuring out how to do so. It looks like sinon.spy() might be the method I want, but it looks like it requires an object, of which I don't have one since it's just a utils file.
My ideal test would look something like this
describe('someFunction fails on an item', () => {
it('returns the array with the rest of the items', () => {
const items = ['hi', 'hello'];
// I want to make it such that, when we go into the getListOfItems code, we return 42 whenever we call someFunction, rather than going into the logic itself.
const someFunctionStub = sinon.stub(someFunction).returns(42);
expect(getListOfItems(items)).toEqual([42, 42]);
});
});
sinon.stub replaces a property on an object...
...and often the object is a module and the property is a function the module exports.
When the module export of a function is stubbed, any code that calls the module export of the function will then call the stub.
It isn't possible to stub someFunction in the code above since processListOfItems isn't calling the module export of someFunction, it is calling someFunction directly.
processListOfItems needs to call the module export of someFunction in order to be able to stub the call.
Here is a simple example to demonstrate using Node.js module syntax:
util.js
exports.func1 = () => {
return 'hello ' + exports.func2(); // <= use the module
}
exports.func2 = () => 'world';
util.test.js
const sinon = require('sinon');
const util = require('./util');
describe('func1', () => {
it('should work', () => {
const stub = sinon.stub(util, 'func2').returns('everyone');
expect(util.func1()).toBe('hello everyone'); // Success!
});
});
...and here is a simple example using ES6 module syntax:
util.js
import * as util from './util'; // <= import module into itself
export const func1 = () => {
return 'hello ' + util.func2(); // <= use the module
}
export const func2 = () => 'world';
util.test.js
import * as sinon from 'sinon';
import * as util from './util';
describe('func1', () => {
it('should work', () => {
const stub = sinon.stub(util, 'func2').returns('everyone');
expect(util.func1()).toBe('hello everyone'); // Success!
});
});
Note that ES6 modules can be imported into themselves since they "support cyclic dependencies automatically".

How to overwrite (or mock) a class method with Jest in order to test a function?

I have an issue with a unit test of a function which calls a class. It seems that it always calls the "official" class instance and not my mocked class. I'm not able to force my function to use my mocked instance...
There is a file with the function I want to test:
const myClass = require('./myClass');
const instance = new myClass();
module.exports.functionToTest = async function () {
// Some stuff...
const value = await instance.myMethod();
// Some stuff that define a result variable (partially with value).
return result;
}
There is a file with my class definition:
module.exports = class myClass {
async myMethod() {
const result = await someStuffWillResolveMaybeTrueOrFalse();
console.log('We used the original myMethod... Mocking has failed.');
return result;
}
}
There is a spec file:
const myFile = require('./myFile');
const myClass = require('./myClass');
describe('My test', async () => {
it('should mock myClass.myMethod in order to return false', () => {
const instance = new myClass();
instance.myMethod = jest.fn().mockResolvedValue(false);
const result = await myFile.functionToTest();
expect(result).toBeTruthy();
}
}
Unfortunately my test is passing (because myMethod return "true") and log "We used the original myMethod... Mocking has failed."
So I want to make my test always fail by mocking that myMethod to return false.
Can you help me? Thanks for your time.
Hm. I've found a solution.
See. A change in my file with the target function.
const myClass = require('./myClass');
// const instance = new myClass(); <== Not here...
module.exports.functionToTest = async function () {
const instance = new myClass(); // <== ...but there.
// Some stuff...
const value = await instance.myMethod();
// Some stuff that define a result variable (partially with value).
return result;
}
And my spec file :
const myFile = require('./myFile');
// I specify to Jest that I'll mock a file
jest.mock('./myClass');
const myClass = require('./myClass');
// I prepare the mock function. In that case a promise wich resolve 'false'
const mMock = jest.fn().mockResolvedValue(false);
// I mock the method 'myMethod' in 'myClass'
myClass.mockImplementation(() => {
return {
myMethod: mMock
};
});
// Then, I just take the test
describe('My test', async () => {
it('should mock myClass.myMethod in order to return false', () => {
const result = await myFile.functionToTest();
expect(result).toBeFalsy();
}
}

sinon stub not replacing function

I'm trying to use sinon stub to replace a function that might take along time. But when I run the tests, the test code doesn't seem to be using the sinon stubs.
Here is the code I'm trying to test.
function takeTooLong() {
return returnSomething();
}
function returnSomething() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('ok')
}, 1500)
})
}
module.exports = {
takeTooLong,
returnSomething
}
and this is the test code.
const chai = require('chai')
chai.use(require('chai-string'))
chai.use(require('chai-as-promised'))
const expect = chai.expect
chai.should()
const db = require('./database')
const sinon = require('sinon')
require('sinon-as-promised')
describe('Mock the DB connection', function () {
it('should use stubs for db connection for takeTooLong', function (done) {
const stubbed = sinon.stub(db, 'returnSomething').returns(new Promise((res) => res('kk')));
const result = db.takeTooLong()
result.then((res) => {
expect(res).to.equal('kk')
sinon.assert.calledOnce(stubbed);
stubbed.restore()
done()
}).catch((err) => done(err))
})
I get an assertion error
AssertionError: expected 'ok' to equal 'kk'
+ expected - actual
-ok
+kk
What am I doing wrong? Why isn't the stub being used ? The test framework in Mocha.
Sinon stubs the property of the object, not the function itself.
In your case you are exporting that function within an object.
module.exports = {
takeTooLong,
returnSomething
}
So in order to properly call the function from the object, you need to replace your function call with the reference to the export object like :
function takeTooLong() {
return module.exports.returnSomething();
}
Of course based on your code, you can always refactor it :
var exports = module.exports = {
takeTooLong: function() { return exports.returnSomething() }
returnSomething: function() { /* .. */ }
}
You might want to have a look at Proxyquire to stub/spy directly exported functions.
https://www.npmjs.com/package/proxyquire/

Testing for method calls using sinon on module.exports methods

I'm trying to test if a specific method is called given certain conditions using mocha, chai and sinon. Here is the code:
function foo(in, opt) {
if(opt) { bar(); }
else { foobar(); }
}
function bar() {...}
function foobar() {...}
module.exports = {
foo: foo,
bar: bar,
foobar:foobar
};
Here is the code in my test file:
var x = require('./foo'),
sinon = require('sinon'),
chai = require('chai'),
expect = chai.expect,
should = chai.should(),
assert = require('assert');
describe('test 1', function () {
it('should call bar', function () {
var spy = sinon. spy(x.bar);
x.foo('bla', true);
spy.called.should.be.true;
});
});
When I do a console.log on the spy it says it wasn't called even thou with manual logging in the bar method I'm able to see it gets called. Any suggestions on what I might be doing wrong or how to go about it?
Thanks
You've created a spy, but the test code doesn't use it. Replace the original x.bar with your spy (don't forget to do cleanup!)
describe('test 1', function () {
before(() => {
let spy = sinon.spy(x.bar);
x.originalBar = x.bar; // save the original so that we can restore it later.
x.bar = spy; // this is where the magic happens!
});
it('should call bar', function () {
x.foo('bla', true);
x.bar.called.should.be.true; // x.bar is the spy!
});
after(() => {
x.bar = x.originalBar; // clean up!
});
});

Mocking JavaScript constructor with Sinon.JS

I'd like to unit test the following ES6 class:
// service.js
const InternalService = require('internal-service');
class Service {
constructor(args) {
this.internalService = new InternalService(args);
}
getData(args) {
let events = this.internalService.getEvents(args);
let data = getDataFromEvents(events);
return data;
}
}
function getDataFromEvents(events) {...}
module.exports = Service;
How do I mock constructor with Sinon.JS in order to mock getEvents of internalService to test getData?
I looked at Javascript: Mocking Constructor using Sinon but wasn't able to extract a solution.
// test.js
const chai = require('chai');
const sinon = require('sinon');
const should = chai.should();
let Service = require('service');
describe('Service', function() {
it('getData', function() {
// throws: TypeError: Attempted to wrap undefined property Service as function
sinon.stub(Service, 'Service').returns(0);
});
});
You can either create a namespace or create a stub instance using sinon.createStubInstance (this will not invoke the constructor).
Creating a namespace:
const namespace = {
Service: require('./service')
};
describe('Service', function() {
it('getData', function() {
sinon.stub(namespace, 'Service').returns(0);
console.log(new namespace.Service()); // Service {}
});
});
Creating a stub instance:
let Service = require('./service');
describe('Service', function() {
it('getData', function() {
let stub = sinon.createStubInstance(Service);
console.log(stub); // Service {}
});
});

Categories