nodejs sinonjs stub on class construcor - node.js

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

Related

Resolution method is overspecified when testing with Mocha and Supertest for Node.js [duplicate]

After the upgrade, Mocha can not even run a simple test here is the code
const assert = require('assert');
it('should complete this test', function (done) {
return new Promise(function (resolve) {
assert.ok(true);
resolve();
})
.then(done);
});
I took this code from here
I understood that it now throws an exception Error: Resolution method is overspecified. Specify a callback * or * return a Promise; not both.
But how to make it work? I did not understand. I have
node -v 6.9.4
mocha -v 3.2.0
How to run this code are now in a new and correct format?
Just drop
.then(done); and replace function(done) with function()
You are returning a Promise so calling done is redundant as it said in error message
In the elder versions you had to use callback in case of async methods like that
it ('returns async', function(done) {
callAsync()
.then(function(result) {
assert.ok(result);
done();
});
})
Now you have an alternative of returning a Promise
it ('returns async', function() {
return new Promise(function (resolve) {
callAsync()
.then(function(result) {
assert.ok(result);
resolve();
});
});
})
But using both is misleading
(see for example here https://github.com/mochajs/mocha/issues/2407)
Mocha allows to either use a callback:
it('should complete this test', function (done) {
new Promise(function (resolve) {
assert.ok(true);
resolve();
})
.then(done);
});
OR return a promise:
it('should complete this test', function () {
return new Promise(function (resolve) {
assert.ok(true);
resolve();
});
});
// Or in the async manner
it('should complete this test', async () => {
await Promise.resolve();
assert.ok(true);
});
You can't do both.
I had to removed the done from the function parameter and the done() of the function call
Before
before(async function (done) {
user = new User({ ...});
await user.save();
done()
});
After
before(async function () {
user = new User({ ...});
await user.save();
});
These works for me
I had this same issue. A lot of times Mocha is paired with another library called Chai. Chai has a package called "chai-as-promised". It gives you the super simple ability to write less code and test promises. In your case of just testing if a promise resolves, it seems perfect.
const chai = require('chai');
const chaiAsPromised = require("chai-as-promised");
const should = require("chai").should();
chai.use(chaiAsPromised);
describe("Testing with correct syntax and non repeated names", () => {
it("Should give us a positive response", () => {
graphQL.sendToGQL(model,"specialEndpoint").should.eventually.be.an("Object");
})
})
An example of async functions with done breaking.
Failure Case
it('If the credentials exists in the system it should return the token generated against it.', async (done) => {
let aObj = await admin.createAdmin();
chai.request(server)
.post("/authenticate")
.set("Content-Type", "application/x-www-form-urlencoded")
.send({username: aObj.login,password:aObj.password})
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a("string");
done();
});
});
Success Case
it('If the credentials exists in the system it should return the token generated against it.', async () => {
let adminObj = await admin.createAdmin();
chai.request(server)
.post("/auth/login")
.set("Content-Type", "application/x-www-form-urlencoded")
.send({username: adminObj.login,password:adminObj.password})
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a("string");
// done();
});
});
If you don't have callbacks, prior answers (which suggest deleting the done) is correct.
If need to both await some external promise, and then exercise a callback/errback-based implementation in your test, that solution doesn't help you.
You can use a library like pify to convert the callback API to use promises.
Alternatively, you can use a Latch in your callback:
it("test", async () => {
const l = new Latch()
const v = await promiseValue()
s.methodThatTakesCallback((err, result) => {
expect(result).to.eql(expected)
l.resolve() // < notifies mocha your test is done
})
return l.promise
})
In TypeScript, here's a very stripped-down Latch implementation:
/**
* Simple one-count concurrent barrier
*/
export class Latch {
readonly promise: Promise<void>
resolve!: () => void
constructor() {
this.promise = new Promise<void>(resolve => (this.resolve = resolve))
}
}
Just emit done callback completely and use async instead.
(This implementation is based on an express api running on firebase functions, using a custom jsonwebtoken)
const { FIREBASE_UID } = require('dotenv').config()?.parsed
const chai = require('chai');
const chaiHttp = require('chai-http');
const server = require('../lib/api').API;
const should = chai.should();
const expect = chai.expect
chai.use(chaiHttp)
const test = chai.request(server).keepOpen()
// get your token with an earlier mock request and store to a var
describe('Just checking a token', () => {
let some_token
it('should print custom jwt for testing, status: 200'), async () => {
try {
const res = await test.get(`/createCustomFirebaseToken/${FIREBASE_UID}`).send()
res.should.exist
res.should.have.status(200);
res.should.have.json
some_token = (JSON.parse(res.text)).token
} catch (error) {
throw error
}
}
it('should print details:PING, status:200'), async () => {
try {
const res = await test.get('/').set('Authorization',`Bearer ${some_token}`)
.send()
res.should.exist
res.should.have.status(200);
res.should.have.json
const { details, status } = JSON.parse(res.text)
expect(details).to.equal('PING')
expect(status).to.equal(200)
} catch (error) {
throw error
}
}
after(() => test.close())
})

In Node js, why is my controller.js not showing the data after invoking a model.js function

I'm new to Nodejs and i've fetched a json file using require and fs, and using MVC , i have a model.js that has the function that reads the json file, but when the controller invokes the model function, the data is not shown (console.log(data) in the controller, but console.logged in the model.js. Here is my code:
controller.js
exports.renderHomePage = (req, res) => {
apiServerModel.loadTeams()
.then(function (data) {
console.log(data);
console.log("This is inside controller")
res.render("index", { // output as string
teamsList: `${JSON.stringify(data, null, 2)}` // A property called teamList to be displayed on the browser
//teamsList: teamView.TeamsView(`${JSON.stringify(data, null, 2)}`)
})
})
.catch(error => console.log(error))
}
model.js
'use strict';
const fs = require('fs');
class TeamsModel {
static async loadTeams() {
try {
await fs.readFile('./json/prov-nodes.json', (err, rawData) => {
if (err) throw err;
let teams = JSON.parse(rawData);
return teams;
//console.log(teams);
});
} catch (error) {
console.log(error)
}
console.log('This is after the read call');
}
}
exports.TeamsModel = TeamsModel;
first of all you should read about Callbacks VS Promises VS Async/Await in node.js.
when you use async/await, don't have a callback so:
use fs/promises instead of fs, because you used await for fs in your loadTeams function.
'use strict';
const fs = require('fs/promises');
class TeamsModel {
static async loadTeams() {
console.log("hi")
try {
let rawData = await fs.readFile('./json/prov-nodes.json')
let teams = JSON.parse(rawData);
return teams;
} catch (error) {
console.log(error)
}
console.log('This is after the read call');
}
}
module.exports = TeamsModel;

How to unit test nested function in node js?

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.

Mock static methods es6 classes to test

I have a lib say 'services.js'
class Service {
static doSomething() {
return Promise.resolve({});
}
}
I have another handler 'handler.js'
let Service = require('./Service');
exports.search = (req, res) => {
Service.doSomething().then(result => {
res.send(result);
}).catch(err=>{
res.status(500).send(err);
});
}
I want to test my handler. To do so I tried stubbing the static method in Service class like:
let Service = require(path to services.js),
Handler = require(path to handler.js),
http_mocks = require('node-mocks-http'),;
describe("handler tests : ", () => {
before(()=>{
sinon.stub(Service, 'doSomething').callsFake(()=>{});
})
it('should succeed', (done) => {
let response = buildResponse();
let request = http_mocks.createRequest({
method: 'GET',
url: '/search?q=2',
});
response.on('end', function() {
let result = JSON.parse(response._getData());
//Some validation
done();
});
Handler.search(request, response);
done();
})
})
I get TypeError: Service.doSomething is not a function. Is there an alternative? I tried using mockery as well. Am I missing something
I found a bug in the test code. Service.doSomething() returns promise but in the test file, we stub it using callsFake and it doesn't return promise
We might use resolves for this as in
before(() => {
sinon.stub(Service, 'doSomething').resolves('asik');
});
NOTE: resolves has been supported since Sinon 4.

node.js stubbing AWS S3 method in request spec with sinon

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()
})
})
})
})

Resources