Unit testing: how to stub a wrapper function - javascript

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)

Related

Jest call default mock from a file

I have this code on my test:
//app.test.js
jest.mock('notistack', () => ({
useSnackbar: jest.fn(),
}));
jest
.spyOn(notistack, 'useSnackbar')
.mockImplementation(() => ({ enqueueSnackbar }));
How can I move this function to a single file? all of my test contains this same function and I think its redundant to keep copy paste this code. I've tried moving this to other file and make function so I can import it
// helper.js
export function snackbar() {
jest.mock('notistack', () => ({
useSnackbar: jest.fn(),
}));
jest
.spyOn(notistack, 'useSnackbar')
.mockImplementation(() => ({ enqueueSnackbar }));
}
// app.test.js
import {snackbar} from 'helper.js';
snackbar();
// othercode for testing;
but everytime I run it, it always return me Cannot spyOn on a primitive value; undefined given.
how can I call it properly?
have a look at Manual Mocks Guide in Jest documentation.
You can create a folder called __mocks__ and replicate the modules that you want to mock there.
For example, I have a main.js file with:
let { Chance } = require("chance");
let chance = Chance();
function createUser() {
return {
id: chance.guid(),
name: chance.name(),
age: chance.age(),
};
}
module.exports = { createUser };
I create a main.test.js file with:
let { createUser } = require("./main");
test("creates a new user", () => {
let scenario = createUser();
expect(scenario).toMatchObject({
id: "zxcv",
name: "Foo",
age: 20,
});
});
Now I can create a file in __mocks__/chance.js with the same signature to mock my module chance:
let chance = {};
chance.Chance = function () {
return {
guid: () => "zxcv",
name: () => "Foo",
age: () => 20,
};
};
module.exports = chance;
Now, every file you test that require/import chance, will use this mock by default.

Sinon stubbing giving 'is not a function' error

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>

Mock shell command output in Jest

I'm writing a cli tool and I'm trying to write tests for it in Jest. I have some functions that call out to git, but I need to mock the returns from those calls or they aren't going to be consistent.
The code I'm using to call out to the shell looks like this.
import { exec } from "child_process";
function execute(command) {
return new Promise((resolve, reject) => {
exec(command, resolve);
});
}
export const getGitDiff = function () {
return execute("git diff")
};
How can I write a test for that in Jest?
What I tried was
import { getGitDiff } from './getGitDiff';
describe('get git diff', () => {
it('should send "git diff" to stdin', () => {
const spy = jest.spyOn(process.stdin, 'write');
return getGitDiff().then(() => {
expect(spy).toHaveBeenCalled();
})
});
});
I ended up creating a new file called child_process.js and using the genMockFromModule functionality in Jest to stub the whole module and reimplemented some of the functions like this
const child_process = jest.genMockFromModule('child_process');
const mockOutput = {}
const exec = jest.fn().mockImplementation((command, resolve) => {
resolve(mockOutput[command]);
})
const __setResponse = (command, string) => {
mockOutput[command] = string;
}
child_process.exec = exec
child_process.__setResponse = __setResponse;
module.exports = child_process;
and I have a test like
const child_process = jest.genMockFromModule('child_process');
const mockOutput = {}
const exec = jest.fn().mockImplementation((command, resolve) => {
resolve(mockOutput[command]);
})
const __setResponse = (command, string) => {
mockOutput[command] = string;
}
child_process.exec = exec
child_process.__setResponse = __setResponse;
module.exports = child_process;

spying on bunyan log - NodeJS

Is there any way in which I can spy on the bunyan log to ensure I print out what I expect?
MyFile.js
const bunyan = require('bunyan');
const log = bunyan.createLogger({name: 'FailureAuditService'});
class someClass {
someFunct() {
if(x) {
log.warn('something happened');
}
}
}
Test
const service = require(../MyFile);
describe('test something', () => {
it('Will test the bunyan log', res => {
let consoleLog = sinon.spy(log, 'createLogger');
let x = true;
service.someClass(x).then(res => {
let expected = 'something happened';
consoleLog.should.equal(expected);
});
});
})
Yes, with Jest it's quite easy:
let spyLogWarn = jest.spyOn(require('bunyan').prototype, 'warn')
// ...
expect(spyLogWarn).toHaveBeenCalled()
I worked around this with the following:
const mockReq = require('mock-require);
...
let infoStub = sinon.stub();
let warnStub = sinon.stub();
logStubs = {
info: infoStub,
warn: warnStub
// any other log methods you wish to use
};
mockReq('bunyan', {
createLogger() {
return logStubs;
}
});
...
I then used to mockReq.reRequire() function later on to reset the cache of the service I was wanting to mock.
To assert the actual content of the logs:
let infoLog = infoStub.firstCall.args[0];
let warnLog = warnStub.firstCall.args[0];
With this, I could assert them to equal whatever I expected.
For Sinon you may write something like:
const bunyan = require('bunyan');
sinon.stub(bunyan.prototype);
// or
sinon.stub(bunyan.prototype, 'fatal');
// or
sinon.stub(bunyan.prototype, 'fatal').callThrough();
And in asserting
sinon.assert.calledOnce(bunyan.prototype.fatal);

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