How to test a helper functions used in controller in Sinon.js - javascript

I am using sinon.js to test my API.
I would like to test the order from my helper functions that are being called.
controller.js
exports.controllerFunction = async (req, res) => {
const function1Results = await function1(paramm);
const function2Results = await function2(param, function1Results);
return res.send(function2Results);
};
helpers.js
exports.function1 = function(param) {
return param;
}
exports.function2 = function(param, func) {
return param;
}
unitTest.js
const controller = require('./controller.js')
const helpers = require('./helpers.js')
describe('Unit test cycle', () => {
beforeEach(() => {
// Spies
sinon.spy(controller, 'controllerFunction');
sinon.spy(helpers, 'function1');
sinon.spy(helpers, 'function2');
// Function calls
controller.controllerFunction(this.req, this.res)
})
afterEach(() => {
sinon.restore();
})
this.req = {}
this.res = {}
it('should call getAvailability', (done) => {
expect(controller.controllerFunction.calledOnce).to.be.true
expect(helpers.function1.calledOnce).to.be.true
expect(helpers.function2.calledOnce).to.be.true
});
})
expect(controller.controllerFunction.calledOnce).to.be.true
is returning in as true
expect(helpers.function1.calledOnce).to.be.true
expect(helpers.function2.calledOnce).to.be.true
and is coming in as false.
Because my helper functions are being used in the controller they should be called as well, yet they are not.
So how do I test if my helper functions are being called as well when the controller is tested?

I will try to give a shot. Since your function is async I think your block for testing should be made to await for it.
I recommend moving the function call inside the it block of commands. beforeEach is usually used for setup, afterEach is used to clear some data/mocks (also known as tear).
Try
it('should call getAvailability', async (done) => {
// When
await controller.controllerFunction(this.req, this.res)
// Assert
expect(controller.controllerFunction.calledOnce).to.be.true
expect(helpers.function1.calledOnce).to.be.true
expect(helpers.function2.calledOnce).to.be.true
done && done()
});
Don't forget to remove the function call from beforeEach.

Related

How can I mock a function of other file that return a promise?

I have function below. I can't test this function using Jest framework.
The function:
exports.example = async function (data) {
let utility;
if (data.flag) {
utility = require('./utility');
} else {
utility = require(Runtime.giveFunctions()['utility'].path);
}
const valueFromUtility = await utility.getValue(data);
.
.
.
From the test I pass the data object that contains several properties, including:
flag: if it is true I require the utility.js file and then I call the getValue (data) function, if it is false I get the path of the utility file that contains the getValue function and then I call the getValue (data) function.
If from the test pass data.flag === true the test is fine, otherwise (data.flag === false) the test fails telling me "utility.getValue is not a function".
In the utility.js file the getValue function returns a promise, as a parameter it has the data object which contains the mocked functions that will be called in getValue.
The test I wrote is the following:
test('Test with flag === false', async () => {
jest.mock('../functions/utility', () => () => false);
await example(data);
});
I also tried to mock the utility getValue function to return a resolved promise:
test('Test with flag === false', async () => {
jest.mock('../functions/utility', () => jest.fn().mockReturnValue(Promise.resolve('Some values')));
await example(data);
});
Finally, I mocked as follows:
const Runtime = {
giveFunctions: jest.fn().mockReturnValue({
utility: jest.fn().mockReturnValue({
path: jest.fn()
})
})
};
window.Runtime = Runtime;
In the utility file I have only getValue function.
How can I solve this problem? Thanks everyone in advance

Jest test the resolve reject callback

I have this function which calls an util function for api calls. The util function resolves or rejects based on the api result.
Now, I need to unit test the callback functions which has the following structure.
`theClassMethod : () => {
return utilMethod().then(
result => { this.functionOne() //Test this function is called },
error => { this.functionTwo() //Test this function is called }
)
}`
The util method returns a promise like below:
utilFunc = (data :string) :Promise<ResultData[]> => {
return new Promise(async (resolve, reject) => {
try{
resolve(data)
}catch{
reject(error)
}
})
}
https://codesandbox.io/s/vjnwy1zw75?fontsize=14
What I tried:
Mocked the util method to resolve/reject. Call the class method and do assertions. It doesn't work and the test always passes as a false positive.
I have spend much time looking for a similar problem. Most questions here are to test the code like:
theClassMethod : () => { utilMethod.then().catch()}
The problem I am trying to solve is to test the resolve, reject callbacks in the then block then(function1, function2). That the code block inside function1 has to be tested that it calls some intended functions.
The approach you are describing (mocking utilMethod to resolve/reject) is a good approach.
Here is a simple working example to get you started:
Note: I implemented functionOne as a class method and functionTwo as an instance property to show how to spy on both types of functions:
util.js
export const utilMethod = async () => 'original';
code.js
import { utilMethod } from './util';
export class MyClass {
functionOne() { } // <= class method
functionTwo = () => { } // <= instance property
theClassMethod() {
return utilMethod().then(
result => { this.functionOne() },
error => { this.functionTwo() }
);
}
}
code.test.js
import { MyClass } from './code';
import * as util from './util';
test('theClassMethod', async () => {
const mock = jest.spyOn(util, 'utilMethod');
const instance = new MyClass();
const functionOneSpy = jest.spyOn(MyClass.prototype, 'functionOne'); // <= class method
const functionTwoSpy = jest.spyOn(instance, 'functionTwo'); // <= instance property
mock.mockResolvedValue('mocked value'); // <= mock it to resolve
await instance.theClassMethod();
expect(functionOneSpy).toHaveBeenCalled(); // Success!
mock.mockRejectedValue(new Error('something bad happened')); // <= mock it to reject
await instance.theClassMethod();
expect(functionTwoSpy).toHaveBeenCalled(); // Success!
});

How do I unit test the result of a 'then' of a promise in JavaScript?

I'm using TypeScript to write a very simple service that utilizes the AWS SDK. My Jest unit tests are passing, but the coverage reports are saying that the line 'return result.Items' is not covered. Can anyone tell why this is? Is it a bug in jest?
// service file
/**
* Gets an array of documents.
*/
function list(tableName) {
const params = {
TableName: tableName,
};
return docClient
.scan(params)
.promise()
.then((result) => {
return result.Items;
});
}
// test file
const stubAwsRequestWithFakeArrayReturn = () => {
return {
promise: () => {
return { then: () => ({ Items: 'fake-value' }) };
},
};
};
it(`should call docClient.scan() at least once`, () => {
const mockAwsCall = jest.fn().mockImplementation(stubAwsRequest);
aws.docClient.scan = mockAwsCall;
db.list('fake-table');
expect(mockAwsCall).toBeCalledTimes(1);
});
it(`should call docClient.scan() with the proper params`, () => {
const mockAwsCall = jest.fn().mockImplementation(stubAwsRequest);
aws.docClient.scan = mockAwsCall;
db.list('fake-table');
expect(mockAwsCall).toBeCalledWith({
TableName: 'fake-table',
});
});
it('should return result.Items out of result', async () => {
const mockAwsCall = jest
.fn()
.mockImplementation(stubAwsRequestWithFakeArrayReturn);
aws.docClient.get = mockAwsCall;
const returnValue = await db.get('fake-table', 'fake-id');
expect(returnValue).toEqual({ Items: 'fake-value' });
});
The line not covered is the success callback passed to then.
Your mock replaces then with a function that doesn't accept any parameters and just returns an object. The callback from your code is passed to the then mock during the test but it doesn't call the callback so Jest correctly reports that the callback is not covered by your tests.
Instead of trying to return a mock object that looks like a Promise, just return an actual resolved Promise from your mock:
const stubAwsRequestWithFakeArrayReturn = () => ({
promise: () => Promise.resolve({ Items: 'fake-value' })
});
...that way then will still be the actual Promise.prototype.then and your callback will be called as expected.
You should also await the returned Promise to ensure that the callback has been called before the test completes:
it(`should call docClient.scan() at least once`, async () => {
const mockAwsCall = jest.fn().mockImplementation(stubAwsRequest);
aws.docClient.scan = mockAwsCall;
await db.list('fake-table'); // await the Promise
expect(mockAwsCall).toBeCalledTimes(1);
});
it(`should call docClient.scan() with the proper params`, async () => {
const mockAwsCall = jest.fn().mockImplementation(stubAwsRequest);
aws.docClient.scan = mockAwsCall;
await db.list('fake-table'); // await the Promise
expect(mockAwsCall).toBeCalledWith({
TableName: 'fake-table',
});
});
The Library chai-as-promised is worth looking at.
https://www.chaijs.com/plugins/chai-as-promised/
Instead of manually wiring up your expectations to a promise’s
fulfilled and rejected handlers.
doSomethingAsync().then(
function (result) {
result.should.equal("foo");
done();
},
function (err) {
done(err);
}
);
you can write code that expresses what you really mean:
return doSomethingAsync().should.eventually.equal("foo");

how do i test my async jasmine/nodejs/promise code using Spies

I have a module (example has been simplified) called process-promise which has a single function that takes a Promise as input and processes it - it also calls other functions using modules outside it as follows:
//<process-promise.js>
let User = require('user-module');
let processPromise = (promiseObj) => {
let user = new User();
promiseObj.then((full_name) => {
const [ fname, sname ] = full_name.split(' ');
if (fname && sname) {
user.setDetails(fname, sname);
} else{
console.log('nothing happened');
}
}).catch((err) => {
console.log(err.message);
});
};
module.exports = {
processPromise
};
I am trying to unit test the above function using Jasmine, Rewire and Jasmine spies as per following code
let rewire = require('rewire');
let mod = rewire('process-promise');
describe('process-promise module', () => {
beforeEach(() => {
this.fakeUser = createSpyObj('fake-user', ['setDetails']);
this.fakeUserMod = jasmine.createSpy('fake-user-mod');
this.fakeUserMod.and.returnValue(this.fakeUser)
this.revert = mod.__set__({
User: this.fakeUserMod
});
});
afterEach(() => {
this.revert();
});
it('fakeUser.setDetails should be called', (done) => {
mod.processPromise(Promise.resolve('user name'));
done();
expect(this.fakeUser.setDetails).toHaveBeenCalledWith('user','name');
});
});
I expect that the Spy this.fakeUser.setDetails should get called but i get the message from Jasmine "Expected spy fake-user.setAll to have been called with [ 'user', 'name' ] but it was never called." - the problem seems to be the fact the promise is Async but i've included the done function as other SO questions have suggested but this doesn't seem to resolve the problem for me. What's the issue with my code? most other SO questions relate to angular so don't help with my problem.
You are on the right track, the promise is asynchronous and then done function in your test is called before the promise resolved to a value. The done function is used as a callback to tell the test engine, that all your asynchronous code has completed. It should be called after the promise resolved to a value (or failed for that matter).
In order to do that, you'd need to make the following adjustments to your code:
//<process-promise.js>
let User = require('user-module');
let processPromise = (promiseObj) => {
let user = new User();
// return a promise, to allow a client to chain a .then call
return promiseObj.then((full_name) => {
const [ fname, sname ] = full_name.split(' ');
if (fname && sname) {
user.setDetails(fname, sname);
} else{
console.log('nothing happened');
}
}).catch((err) => {
console.log(err.message);
});
};
module.exports = {
processPromise
};
The test would then look like this:
it('fakeUser.setAll should be called', (done) => {
mod.processPromise(Promise.resolve('user name')).then(() => {
expect(this.fakeUser.setAll).toHaveBeenCalledWith('user','name');
done();
}).catch(done);
});
Be sure to add .catch(done). This will make sure your test fails in case the promise resolves to an error.
Is probable that, by the time your test code execute, the promise has not propagated to the code under test. And simply calling done() doesn't the synchronization magic.
I'm not familiar with rewire so I will share an example using
proxyquire
const proxyquire = require('proxyquire');
describe('process-promise module', () => {
const fakeUser = { setDetails: jasmine.createSpy('setDetails') };
const fakeUserMod = jasmine.createSpy('fake-user-mod').and.returnValue(fakeUser);
const promiseObj = Promise.resolve('user name');
beforeEach((done) => {
const processPromiseMod = proxyquire('process-promise', {
'user-module': fakeUserMod,
});
processPromiseMod.processPromise(promiseObj);
promiseObj.then(() => done());
});
it('fakeUser.setDetails should be called', () => {
expect(fakeUser.setDetails).toHaveBeenCalledWith('user','name');
});
});
Also note that setAll doesn't exist in the fakeUser instance. I guess you mean setDetails instead of setAll.

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