I have a function for which I am writing unit test but that function is calling another function and there I am not able to mock/stub that function.
for example :
function getValue( param1, param2, callback){
getData(param1, param3).then( response) => {
return callback();
}, (err) => {
return callback();
});
}
So I don't know how to mock getData() function.
Here is a working example that demonstrates what you are trying to do:
lib.js
function getData(param1, param2) {
return fetch('someUrl'); // <= something that returns a Promise
}
exports.getData = getData;
code.js
const lib = require('./lib');
export function getValue(param1, param2, callback) {
return lib.getData(param1, param2).then(response => {
callback(response);
}).catch(err => {
callback(err);
});
}
exports.getValue = getValue;
code.test.js
const sinon = require('sinon');
const lib = require('./lib');
const { getValue } = require('./code');
describe('getValue', () => {
it('should do something', async () => {
const stub = sinon.stub(lib, 'getData');
stub.resolves('mocked response');
const callback = sinon.spy();
await getValue('val1', 'val2', callback);
sinon.assert.calledWithExactly(stub, 'val1', 'val2'); // Success!
sinon.assert.calledWithExactly(callback, 'mocked response'); // Success!
});
});
Update
OP added in the comments that they can't use async / await and are exporting the function using module.exports = getData;.
In that case the module export is the function and the entire module needs to be mocked with something like proxyquire.
The assertions should be done in a then callback and the test should return the resulting Promise so mocha knows to wait for it to resolve.
Updated example:
lib.js
function getData(param1, param2) {
return fetch('someUrl'); // <= something that returns a Promise
}
module.exports = getData;
code.js
const getData = require('./lib');
function getValue(param1, param2, callback) {
return getData(param1, param2).then(response => {
callback(response);
}).catch(err => {
callback(err);
});
}
module.exports = getValue;
code.test.js
const sinon = require('sinon');
const proxyquire = require('proxyquire');
describe('getValue', () => {
it('should do something', () => {
const stub = sinon.stub();
stub.resolves('mocked response');
const getValue = proxyquire('./code', { './lib': stub });
const callback = sinon.spy();
return getValue('val1', 'val2', callback).then(() => {
sinon.assert.calledWithExactly(stub, 'val1', 'val2'); // Success!
sinon.assert.calledWithExactly(callback, 'mocked response'); // Success!
});
});
});
function getValue( param1, param2, callback){
getData(param1, param3).then( response) => {
callback(response);
});
}
getvalue(param1, param2, function(error, response)) {
console.log(response)
}
It might help you.
Related
Running the code bellow I just get "undefined". It seems like the async.parallel call executes the final function before the 4 parallel functions are finished.
function classify(filename) {
var stack = [];
var functionOne = async function(callback){
//call to an API
res = await classifierOne(filename);
callback(null, res);
}
var functionTwo = async function(callback){
//call to an API
res = await classifierTwo(filename);
callback(null, res);
}
var functionThree = async function(callback){
//call to an API
res = await classifierThree(filename);
callback(null, res);
}
var functionFour = async function(callback){
//call to an API
res = await classifierFour(filename);
callback(null, res);
}
stack.push(functionOne);
stack.push(functionTwo);
stack.push(functionThree);
stack.push(functionFour);
async.parallel(stack, function(err, res){
console.log(res[0]);
})
}
If you are using async with version 3.x, we have a big change - Support async/await , with the version, with tasks which have been wrap to a Promise (when you use async/await keyword) then the callback will be disable, you need to return value in the tasks instead of call callback(null, value).
This mean, to fix your case, the code will come to:
function classify(filename) {
var stack = [];
var functionOne = async function(){ // remove callback param
//call to an API
res = await classifierOne(filename);
return res; // return value
}
var functionTwo = async function(){
//call to an API
res = await classifierTwo(filename);
return res;
}
var functionThree = async function(){
//call to an API
res = await classifierThree(filename);
return res;
}
var functionFour = async function(){
//call to an API
res = await classifierFour(filename);
return res;
}
stack.push(functionOne);
stack.push(functionTwo);
stack.push(functionThree);
stack.push(functionFour);
async.parallel(stack, function(err, res){
console.log(res[0]);
})
}
P/s: My suggestion is using Promise.all for this case. Say no to callback.
async function classify(filename) { // Make this function become a async function
var stack = [];
var functionOne = async function(){ // remove callback param
//call to an API
res = await classifierOne(filename);
return res; // return value
}
var functionTwo = async function(){
//call to an API
res = await classifierTwo(filename);
return res;
}
var functionThree = async function(){
//call to an API
res = await classifierThree(filename);
return res;
}
var functionFour = async function(){
//call to an API
res = await classifierFour(filename);
return res;
}
stack.push(functionOne);
stack.push(functionTwo);
stack.push(functionThree);
stack.push(functionFour);
const result = await Promise.all(stack); // Wait until all "task" finish
console.log(result);
return result; // Return the result of all tasks
}
// Call classify function inner a async function
const result = await classify('your_file_name'); // await keyword
console.log(result);
I am not too sure about the async npm library but on top of it looks like it doesn't work when you have a callback function as async. If you try it using native promises, it will work. Something like
function classify(filename) {
var stack = [];
var functionOne = function(callback) {
//call to an API
classifierOne(filename).then(res => callback(null, res));
};
var functionTwo = function(callback) {
classifierTwo(filename).then(res => callback(null, res));
};
var functionThree = function(callback) {
classifierThree(filename).then(res => callback(null, res));
};
var functionFour = function(callback) {
classifierFour(filename).then(res => callback(null, res));
};
stack.push(functionOne);
stack.push(functionTwo);
stack.push(functionThree);
stack.push(functionFour);
async.parallel(stack, function(err, res) {
console.log(res);
});
}
Your methods like i.e. functionOne missed the return and didn't a callback. The below returns ["1","2","3","4"].
const async = require('async');
const classifierOne = async ()=> "1"
const classifierTwo = async ()=> "2"
const classifierThree = async ()=> "3"
const classifierFour = async ()=> "4"
const classify = filename => {
var stack = [];
var functionOne = async ()=> classifierOne(filename);
var functionTwo = async ()=> classifierTwo(filename);
var functionThree = async ()=> classifierThree(filename);
var functionFour = async ()=> classifierFour(filename);
stack.push(functionOne);
stack.push(functionTwo);
stack.push(functionThree);
stack.push(functionFour);
async.parallel(stack, (err, res)=>{
console.log(res);
})
}
classify("x");
I'm using nodejs exif library to retrieve metadata from JPEG files.
this lib is used this way :
import * as exif from 'exif'
new exif.ExifImage('path_to_file.jpg', function(err, metadata){ ... })
I've found everywhere how to stub a class method using sinon, pretty simple.
But I don't get how to stub this class constructor so that metadata (or err if I want to test failing case) will be the stubbed value I need to perform my test.
We can still use Sinon with callsFake function. Here is the example:
// src.js
const exif = require("exif");
function readImage() {
// convert to promise for easier testing
return new Promise((resolve, reject) => {
new exif.ExifImage("path_to_file.jpg", function(err, metadata) {
if (err) {
reject(err);
}
resolve(metadata);
});
});
}
module.exports = { readImage };
meanwhile for test
// test.js
const sinon = require('sinon');
const src = require('./src');
const exif = require('exif');
const expect = require('chai').expect;
describe('test exifimage', () => {
let exifStub;
beforeEach(function() {
exifStub = sinon.stub(exif, 'ExifImage')
})
afterEach(function() {
sinon.restore();
})
it('test when success', async () => {
const metadata = 'cinta';
// mock ExifImage as similar as its function signature
exifStub.callsFake((filename, callback) => {
callback(null, metadata); // error is set as null then we set metadata
});
const response = await src.readImage();
expect(response).to.equal(metadata);
});
it('test when error', async () => {
const errorMessage = 'this is error';
exifStub.callsFake((filename, callback) => {
callback(errorMessage); // specify error, error is defined as first param
});
try {
await src.readImage();
} catch (error) {
expect(error).to.equal(errorMessage);
}
});
});
Hope it helps
I have a function in a class which invokes REST api and returns Promise object.I am able to test Promise object bu I am not sure how we can stub or mock rest api call and test.
Token.js
class Token {
getToken(payload) {
let outahToken = new Promise(function (resolve, reject) {
request('hhtps://xyz.com', function (err, res, body) {
if (err) {
reject(err);
} else {
console.log(res);
resolve(body);
}
})
});
return outahToken;
}
}
module.exports = Token;
Token.test.js
'use strict'
const chai = require("chai");
const expect = chai.expect;
const nock = require('nock');
const sinon = require("sinon");
const Token = require('Token');
describe('Get User tests', () => {
let Token;
beforeEach(() => {
outhController = new Token();
sinon.stub(Token, 'getToken').returns(Promise.resolve({
name: "All"
}));
});
it('Outh test', (done) => {
Token.getToken(payload)
.then(response => {
expect(typeof response).to.equal('object');
done();
});
});
});
We can mock request module with proxyquire and check if it is being called with correct argument. We need proxyquire because request module export a function request() which harder to mock with Sinon only.
And because request method is a callback function, we can use Sinon yields to mock it.
const chai = require('chai');
const sinon = require('sinon');
const proxyquire = require('proxyquire');
const expect = chai.expect;
describe('Token test', function() {
let outhController;
let Token;
let requestStub;
beforeEach(() => {
const err = null;
const res = null;
const body = { name: 'All' };
requestStub = sinon.stub().yields(err, res, body); // create sinon for request function and return response body that we want
Token = proxyquire('Token', { request: requestStub }) // replace original request module with our sinon stub
outhController = new Token();
});
it('Outh test', (done) => {
const payload = {};
outhController.getToken(payload)
.then(response => {
sinon.assert.calledWith(requestStub, 'hhtps://xyz.com');
expect(typeof response).to.equal('object');
expect(response.name).to.equal('All');
done();
});
});
});
Hope it helps
I've got an express based app running on node.js 0.12.2 which uses the s3.headBucket method from aws-sdk 2.1.22 to return a JSON response depending upon whether a particular bucket exists or not.
I've been struggling to directly stub out the call to s3.headBucket with sinon. I've managed to work around this by creating an s3wrapper module which just requires the aws-sdk and instantiates and returns the s3 variable, however, I'm sure this can be done without using the wrapper module and can instead be stubbed directly with sinon, can anyone point me in the right direction?
Below is the currently working code (with the wrapper module s3wrapper.js which I'd like to remove and handle the stubbing in my status_router_spec.js file). In other words, I'd like to be able to call s3.headBucket({Bucket: 'whatever' ... instead of s3wrapper.headBucket({Bucket: ' ... and be able to stub out this s3.headBucket call with my own response.
status_router_spec.js
var chai = require('chai'),
sinon = require('sinon'),
request = require('request'),
myHelper = require('../request_helper')
var expect = chai.expect
var s3wrapper = require('../../helpers/s3wrapper')
describe('My router', function () {
describe('checking the service status', function () {
var headBucketStub
beforeEach(function () {
headBucketStub = sinon.stub(s3wrapper, 'headBucket')
})
afterEach(function () {
s3wrapper.headBucket.restore()
})
describe('when no errors are returned', function () {
it('returns healthy response', function (done) {
// pass null to represent no errors
headBucketStub.yields(null)
request.get(myHelper.appUrl('/status'), function (err, resp, body) {
if (err) { done(err) }
expect(JSON.parse(body)).to.deep.eql({
healthy: true,
message: 'success'
})
done()
})
})
})
})
})
s3wrapper.js
var AWS = require('aws-sdk')
var s3 = new AWS.S3()
module.exports = s3
status_router.js
var Router = require('express').Router
var s3wrapper = require('../helpers/s3wrapper.js')
var router = new Router()
function statusHandler (req, res) {
s3wrapper.headBucket({Bucket: 'some-bucket-id'}, function (err) {
if (err) {
return res.json({ healthy: false, message: err })
} else {
return res.json({ healthy: true, message: 'success' })
}
})
}
router.get(/^\/status\/?$/, statusHandler)
module.exports = router
Answering this question for the benefit of #ippomakunochi who requested a follow up response.
We ended up using rewire to directly set a stub on the s3 library. For example, we stubbed the getObject call for the s3 library using the following:
s3stub = { getObject: sinon.stub(), listObjects: sinon.stub() }
revert = s3.__set__('s3', s3stub)
Here's the complete code:
../../../build/app/helpers/s3
var AWS = require('aws-sdk');
var s3 = new AWS.S3();
module.exports = {
get: function get(options, callback) {
var requestOptions = { Bucket: module.exports.bucket(), Key: options.productId + '.json' };
s3.getObject(requestOptions, function (err, data) {
if (err) { // handle err }
try {
var productData = JSON.parse(data.Body);
} catch (e) {
// handle error
}
return callback(null, productData);
});
}
}
}
test/unit/app/helpers/s3_spec.js
var AWS = require('aws-sdk')
var chai = require('chai')
var sinon = require('sinon')
var sinonChai = require('sinon-chai')
var chaiSubset = require('chai-subset')
var rewire = require('rewire')
var s3 = rewire('../../../build/app/helpers/s3')
chai.use(chaiSubset)
chai.use(sinonChai)
var expect = chai.expect
describe('S3', function () {
var s3stub, revert
beforeEach(function () {
s3stub = { getObject: sinon.stub(), listObjects: sinon.stub() }
revert = s3.__set__('s3', s3stub)
})
afterEach(function () {
revert()
})
describe('#get', function () {
context('when no errors are returned by s3', function () {
it('returns a product', function (done) {
var productResponse = helper.fixture.body('product.json')
s3stub.getObject.yields(null, productResponse)
s3.get({ productId: '1234' }, function (err, res) {
expect(err).to.not.exist
expect(res).to.containSubset({name: 'long sleeve shirt', 'retailer_code': 'retailer-1'})
done()
})
})
})
context('when s3 returns a NoSuchKey error', function () {
it('returns a NotFoundError', function (done) {
var s3Error = AWS.util.error(new Error(), { name: 'NoSuchKey' })
s3stub.getObject.yields(s3Error)
s3.get({ productId: '1234' }, function (err) {
expect(err.message).to.eql('1234 is not found in s3')
expect(err.output.statusCode).to.eql(404)
done()
})
})
})
})
I ran into an issue today where I had passing tests that should have failed. There were some incorrectly stubbed methods using Sinon that failed to stub out the callback correctly. I would have expected execution to fail, but it passed the test, however it stopped execution early because my test messages quit logging to the console at the incorrectly stubbed method invocation.
The code I had was similar to the following:
exports.myFunc = function (req, res) {
var innerFunc = function(callback) {
fs.exists('some/dir', function (exists) {
if (!exists) { // handle error }
else {
logger.log('this prints to the console');
utilModule.stubbedMethod('some/dir', callback);
}
});
};
logger.log('this also prints to the console');
innerFunc(function (err, value) {
logger.log('this does not print to the console');
if (err) {
logger.log('this does not print to the console');
res.status(500).send(err);
} else {
logger.log('this does not print to the console, either');
res.send('success');
}
});
});
// This is the tests, now:
var app = require('../app'),
mod = require('../lib/myModule'),
request = require('supertest'),
chai = require('chai'),
sinon = require('sinon');
describe('For module myModule', function () {
...
describe('#myFunc', function () {
var fs = require('fs'),
util = require('../lib/utilModule');
describe('Happy path', function () {
var expectedResult = "expected";
before(function() {
sinon.stub(fs, 'exists').yields(true, null);
sinon.stub(mod, 'stubbedMethod').yield("some value");
// the above is wrong and should have been yield(null, null, "some value")
});
after(function () { fs.exists.restore(); mod.stubbedMethod.restore(); });
it('Replies with the right value and status 200', function () {
request(app).get('/url').expect(200, 'success').end(function(err, res) {
if (err) throw err;
}
});
});
});
Why did this test pass, and is there some way to prevent it from passing?