Sinon stubbing giving 'is not a function' error - javascript

first time really using sinon and I am having some issues with the mocking library.
All I am trying to do is stub/mock out a function from a dao class called myMethod. Unfortunatly, I am getting the error: myMethod is not a function, which makes me believe I am either putting the await/async keywords in the wrong spots of the test or I don't understand sinon stubbing 100%. Here is the code:
// index.js
async function doWork(sqlDao, task, from, to) {
...
results = await sqlDao.myMethod(from, to);
...
}
module.exports = {
_doWork: doWork,
TASK_NAME: TASK_NAME
};
// index.test.js
const chai = require("chai");
const expect = chai.expect;
const sinon = require("sinon");
const { _doWork, TASK_NAME } = require("./index.js");
const SqlDao = require("./sqlDao.js");
.
.
.
it("given access_request task then return valid results", async () => {
const sqlDao = new SqlDao(1, 2, 3, 4);
const stub = sinon
.stub(sqlDao, "myMethod")
.withArgs(sinon.match.any, sinon.match.any)
.resolves([{ x: 1 }, { x: 2 }]);
const result = await _doWork(stub, TASK_NAME, new Date(), new Date());
console.log(result);
});
With error:
1) doWork
given task_name task then return valid results:
TypeError: sqlDao.myMethod is not a function

Your issue is that you're passing stub to _doWork instead of passing sqlDao.
A stub isn't the object that you just stubbed. It is still a sinon object that you use to define the behaviour of the stubbed method. When you're done with your tests, you use stub to restore stubbed object.
const theAnswer = {
give: () => 42
};
const stub = sinon.stub(theAnswer, 'give').returns('forty two');
// stubbed
console.log(theAnswer.give());
// restored
stub.restore();
console.log(theAnswer.give());
<script src="https://cdnjs.cloudflare.com/ajax/libs/sinon.js/7.2.4/sinon.min.js"></script>

Related

Unit testing: how to stub a wrapper function

I'm new to unit testing and trying to figure out how to stub a wrapper function. I'm using Sinon/Mocha.
If I have a function like:
const user = await User.findOne({ email: email });
I've been able to stub it like so:
const userFindOneStub = sandbox.stub(User, 'findOne')
.returns({
_id: 'userId1234',
companies: [
{
_id: 'companyId1234'
}
]
});
But I've had to create a wrapper for my function to reorder the params for a specific function, using Lodash:
const userWrapper = _.rearg(UserFunction, [0, 1, 2, 3, 5, 4]);
const res = await userWrapper(someargs);
I can stub the UserFunction call, but how would I stub the userWrapper call in a unit test?
By save userWrapper as a module and follow Sinon How to Stub Dependency.
For example you can create userWrapper.js to like this.
// File: userWrapper.js
// This is just sample
const userWrapper = () => {
// In your case is: _.rearg(UserFunction, [0, 1, 2, 3, 5, 4]);
console.log('real');
}
module.exports = { userWrapper };
Then you can use it in your main js to like this.
// File: main.js
const wrapper = require('./userWrapper.js');
module.exports = async function main () {
// In your case: const res = await userWrapper();
wrapper.userWrapper();
}
And finally the test file.
// File: test.js
const sinon = require('sinon');
const wrapper = require('./userWrapper.js');
const main = require('./main.js');
it('Stub userWrapper', () => {
const stub = sinon.stub(wrapper, 'userWrapper').callsFake(() => {
console.log('fake');
});
main();
sinon.assert.calledOnce(stub);
});
When you run it using mocha from terminal:
$ npx mocha test.js
fake
✓ Stub userWrapper
1 passing (3ms)

Node.js unittest Stubbing Own function

Apologies if this was asked before. Here is the module that I'd like to unittest inside file getStuff.js. I'm having difficulty stubbing theresolveThing module used here.
getStuff.js
const resolveThing = require('./resolveThing.js');
module.exports = async function getStuff(target, stuff) {
const { element, test, other } = resolveThing(target);
try {
return element;
} catch (error) {
throw new Error('Did not work.');
}
};
And here is my attempt at the unittest with stubbing using sinon. However when I try to run this it errors out with TypeError: Cannot stub non-existent own property resolveType. Anyone know how I can get this test to work?
const getStuff = require('../com/getStuff');
const resolveThing = require('../com/resolveThing');
const mochaccino = require('mochaccino');
const { expect } = mochaccino;
const sinon = require('sinon');
describe('com.resolveThing', function() {
beforeEach(function () {
sinon.stub(resolveThing, 'resolveThing').returns({element:'a',test:'b',other:'c'});
});
afterEach(function () {
resolveThing.restore();
});
it('Standard message', function() {
const answer = getAttribute('a','b');
expect(answer).toEqual('a');
});
});
sinon.stub(resolveThing, 'resolveThing').returns({element:'a',test:'b',other:'c'});
resolveThing must be an object and 'resolveThing' must be a function in the object, an exception is thrown if the property is not already a function.
I think this is what's hapning in your case.

jest mock a property that is an object and a function at the same time

I am trying to mock a property of an object that acts as an object and as a function at the same time. Here's the code:
index.js
const nock = require('nock');
async function myFunc() {
nock.back.setMode('param1');
const { nockDone } = await nock.back('param1', 'param2');
nock.enableNetConnect('param1');
return nockDone;
}
module.exports = { myFunc }
My goal is to mock the nock object in a way I can assert that when myFunc is called, nock.back is called with param1 and param2.
To do so I have the following test:
index.test.js
const nock = require('nock');
const subjectUnderTest = require('./index');
const nockBackImplementation = jest.fn();
nockBackImplementation.setMode = jest.fn();
const nockBackMock = jest.spyOn(nock, 'back');
nockBackMock.mockImplementation(() => nockBackImplementation);
describe('test', () => {
it('calls nock.back with the proper parameters', () => {
subjectUnderTest.myFunc();
expect(nockBackMock).toHaveBeenCalledWith('param1', 'param2');
});
});
For some reason, the test fails saying that the mock function has not been called and also gives this error:
UnhandledPromiseRejectionWarning: TypeError: nock.back.setMode is not a function
I'm not sure how to properly mock nock.
You're assigning the setMode mock to the function used as implementation, which I think is different from the actual nock.back mock. You can set it correctly this way
const nockBackMock = jest
.spyOn(nock, "back")
.mockResolvedValue({ nockDone: "test" });
nockBackMock.setMode = jest.fn();
// you also need to mock the enableNetConnect to avoid errors
const enableNetConnectMock = jest
.spyOn(nock, "enableNetConnect")
.mockReturnValue({});
describe("test", () => {
it("calls nock.back with the proper parameters", () => {
subjectUnderTest.myFunc();
expect(nockBackMock).toHaveBeenCalledWith("param1", "param2");
});
});

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/

Categories