sinon.mock().expects().atLeast() ... expectation.verify() not working - javascript

I am newbie on node and sinon and I'm having trouble to test the component below. I would like to check if the res.status and res.send were called inside the component.
Component to be tested
module.exports = {
handle: function(promise, res, next, okHttpStatus) {
promise
.then(payload => res.status(okHttpStatus ? okHttpStatus : 200).send(payload))
.catch(exception => next(exception));
}
};
Unit test
const sinon = require("sinon");
const routerPromiseHandler =
require("../../../main/node/handler/PromiseHandler");
describe("Should handle promisse", () => {
it("should handle success promise return", () => {
const successMessage = {message: "Success"};
const promiseTest = new Promise((resolve, reject) => {
resolve(successMessage);
});
let res = {
status: function() {},
send: function() {}
};
const mockRes = sinon.mock(res);
const expectStatus = mockRes.expects("status").withExactArgs(200).atLeast(1)
const expectSend = mockRes.expects("send").withExactArgs(successMessage).atLeast(1)
const spyNext = sinon.spy();
routerPromiseHandler.handle(promiseTest, res, spyNext, 200);
expectStatus.verify();
expectSend.verify();
});
});

I managed to solve the problem. The sinon check wasn't work because the spys were been called inside a promise. To check if the spy were been called. I had to add the assertions inside the then and catch of the promises.
const sinon = require("sinon");
const { mockResponse } = require("mock-req-res");
const routerPromiseHandler = require("../../../main/node/handler/PromiseHandler");
describe("Should handle promisse", () => {
it("should handle success promise return", () => {
const successMessage = { message: "Success" };
const promiseTest = new Promise((resolve, reject) => {
resolve(successMessage);
});
const mockedRes = mockResponse();
const spyNext = {};
routerPromiseHandler.handle(promiseTest, mockedRes, spyNext, 200);
promiseTest.then(() => {
sinon.assert.calledWithMatch(mockedRes.status, 200);
sinon.assert.calledWithMatch(mockedRes.send, successMessage);
})
});
it("should handle error promise return", () => {
const errorMessage = { error: "error" };
const promiseError = new Promise((resolve, reject) => {
reject(errorMessage);
});
const mockedRes = mockResponse();
const nextSpy = sinon.spy();
routerPromiseHandler.handle(promiseError, mockedRes, nextSpy, 200);
promiseError
.then(() => {
// Promise always need the then
})
.catch(exception => {
sinon.assert.calledWithMatch(nextSpy, errorMessage);
})
});
});

Related

How to mock https.get with Jest

I have a function that calls https.get inside a promise which I want to test with Jest.
The function is like this:
const request = (url) => {
return new Promise((resolve, reject) => {
const chunks = [];
https.get(url, (stream) => {
stream
.on('data', (chunk) => {
if( chunk ) {
chunks.push(JSON.parse(chunk));
}
})
.on('error', (err) => {
reject(err);
})
.on('end', () => {
const data = doSomething(chunks);
resolve(data)
});
});
})
}
I want to test that when the function resolves on "end" and rejects on "error";
Currently I have a test like this but because .on("end") doesn't get called, the promise never resolves.
describe("request", () => {
it("Should resolve", async (done) => {
const response = await request("my-url");
expect(response).toEqual("some-data")
})
})
How can I mock events like .on("end") to be called and ensure the promise resolves?
You can do something like this.
// ./request.test.js
jest.mock('https', () => ({
methodToMock: {}
}));
const Stream = require('stream');
const request = require("./request");
const httpsMock = require("https");
describe("request", () => {
it("Should resolve", async () => {
var streamStream = new Stream()
httpsMock.get = jest.fn().mockImplementation((url, cb) => {
cb(streamStream)
streamStream.emit('data', 'some');
streamStream.emit('data', '-');
streamStream.emit('data', 'data');
streamStream.emit('end'); // this will trigger the promise resolve
})
const response = await request("my-url");
expect(response).toEqual("some-data");
})
})
const https = require("https");
const request = (url) => {
return new Promise((resolve, reject) => {
const chunks = [];
https.get(url, (stream) => {
stream
.on('data', (chunk) => {
if (chunk) {
// chunks.push(JSON.parse(chunk));
chunks.push(chunk);
}
})
.on('error', (err) => {
reject(err);
})
.on('end', () => {
// const data = doSomething(chunks);
const data = chunks.join('');
resolve(data)
});
});
})
}
module.exports = request;
Note that jest.mock('https', ...) need to be called before const request = require("./request"); if you want https to be mocked.

Jest: Testing a nested promise

I'm new to jest, and having trouble determining how to test results nested inside promises. Specifically:
myMethod: function (e) {
let self = this
self.resetErrors()
Parser.parseFile(this.form.uploadFile).then(res => {
const hasErrors = self.validFile(res)
if (!hasErrors) {
self.processFile(res)
}
})
}
I'd like to test to ensure that, assuming hasErrors is false, self.processFile fires. Here's my current (failing) best effort:
describe("if the provided data is valid", () => {
it('runs processFile', () => {
const mockProcessFile = jest.fn()
mockParser = jest.fn(() => {
new Promise((resolve, reject) => {
return ValidMockData
}).then((loanData) => {
expect(mockProcessFile).toBeCalled()
})
})
CsvParser.parseFile = mockParser
wrapper.vm.validFile = jest.fn(true)
wrapper.vm.processFile = mockProcessFile
wrapper.vm.store().resolve((data) => {
expect(mockProcessFile).toBeCalled()
})
})
})
At present I'm getting a Cannot read property 'then' of undefined error - which makes sense, but I'm not sure how exactly I'm supposed to crack into expectations inside of a then() call. Any thoughts appreciated
You need to store the Promise you create in the test so you can use await to let the test finishing after it was resolved:
describe("if the provided data is valid", async() => {
it('runs processFile', () => {
const mockProcessFile = jest.fn()
const p = Promise.resolve(ValidMockData)
CsvParser.parseFile = jest.fn(() => p)
wrapper.vm.validFile = jest.fn(true)
wrapper.vm.processFile = mockProcessFile
wrapper.vm.store()
await p
expect(mockProcessFile).toBeCalled()
expect(mockProcessFile).toBeCalled()
})
})

Unable to unit test firebase function in Sinon

I am trying to unit test a JavaScript promise, that contains a firebase query, using mocha, chai, sinon. I am trying to mock the database using sinon rather than actually making a request to the database. However, I am unable to implement it correctly.
Here is my promise in the file '/services/pr_services:
exports.getUserInfo = (userId) => {
return new Promise((resolve, reject) => {
const userProfile = {};
const userProfileRef = database.ref('profiles').child(userId);
userProfileRef.once('value', (snap) => {
if (snap.exists()) {
const userProfileData = snap.val();
resolve(userProfile);
} else {
reject();
}
});
});
};
The variable database contains the database configuration like credentials, database url, etc
Here is my code for the test case:
const chai = require('chai');
const sinon = require('sinon');
const admin = require('firebase-admin');
const database = require('../database');
const services = require('../services/pr_services');
const should = chai.should();
describe('Database functions', () => {
let adminInitStub;
before(() => {
adminInitStub = sinon.stub(admin, 'initializeApp');
});
describe('get profile info', () => {
it('should return a non empty object', (done) => {
beforeEach(() => {
services.getUserInfo = sinon.stub();
});
afterEach(() => {
services.getUserInfo.reset();
});
const userId = 'jim123';
const snap = {
name: 'Jim Dani',
address: 'Porto'
};
const userProfileRef = database.ref('profiles').child(userId);
userProfileRef.once('value').returns(Promise.resolve(snap));
services.getUserInfo
.then(info => {
info.should.be.a('object');
info.should.equal(snap);
done();
})
.catch(err => {
should.not.exist(err);
done();
});
});
});
after(() => {
adminInitStub.restore();
test.cleanup();
});
});
Can anyone point out where I am going wrong and kindly point me in right direction.
Thanks.

How can I write a unit test with streams, promises and pipes together?

My function is
exports.downloadFromBucket = function(fileKey) {
const localPath = `${process.cwd()}/data/${fileKey}`
return new Promise((resolve, reject) => {
const localFile = fs.createWriteStream(localPath)
const awsStream = s3.getObject({
Bucket: process.env.UPLOAD_BUCKET,
Key: fileKey
})
.createReadStream()
.on('error', (err) => {
logger.info('Error downloading file', err)
return reject(err)
})
.on('finish', () => {
logger.info('Completed downloading')
return resolve(localPath)
})
.pipe(localFile)
})
}
How would I go about writing a unit test for this using mocha and sinon?
This may not be the prettiest solution but assuming you want to mock s3 and fs and test the on('error') and on('finish') behavior:
You could use a custom s3 mocking class, stub the original s3 and fs with sinon and trigger the events you want to test.
// Custom S3 Mocking Library
class S3MockLibrary {
constructor() {
this.events = {};
}
getObject(options) {
return this;
}
createReadStream() {
return this;
}
on(event, func) {
this.events[event] = func;
return this;
}
pipe(file) {
return this;
}
emit(event, err) {
this.events[event](err);
}
}
Test on('finish')
it('should verify', async () => {
const s3Mock = new S3MockLibrary();
const fsStub = sinon.stub(fs, 'createWriteStream').returns('success');
const s3Stub = sinon.stub(s3, 'getObject').returns(s3Mock);
// Emit the finish event async
setTimeout(() => {
s3Mock.emit('finish');
}, 0);
const result = await downloadFromBucket('test');
fsStub.restore();
s3Stub.restore();
sinon.assert.calledOnce(fsStub);
sinon.assert.calledOnce(s3Stub);
assert.equal(result, `${process.cwd()}/data/test`);
});
Test on('error)
it('should fail', async () => {
const s3Mock = new S3MockLibrary();
const fsStub = sinon.stub(fs, 'createWriteStream').returns('success');
const s3Stub = sinon.stub(s3, 'getObject').returns(s3Mock);
setTimeout(() => {
s3Mock.emit('error', 'testError');
}, 0);
let error;
await downloadFromBucket('test').catch((err) => {
error = err;
});
fsStub.restore();
s3Stub.restore();
sinon.assert.calledOnce(fsStub);
sinon.assert.calledOnce(s3Stub);
assert.equal(error, 'testError');
});

How to assert this promise via jest?

I write a test with jest to test one of my middleware.
const asyncAll = (req, res, next) => {
const queue = [
service.exchangeLongTimeToken(req),
service.retrieveUserInfo(req),
];
Promise.all(queue).then((values) => {
res.locals.auth = values[0];
res.locals.user = values[1];
next();
}).catch((err) => {
next(err)
});
};
The test file is like this:
const httpMocks = require('node-mocks-http');
const testData = require('../../testdata/data.json');
describe('Test asyncAll', () => {
let spy1 = {};
let spy2 = {};
const mockNext = jest.fn();
afterEach(() => {
mockNext.mockReset();
spy1.mockRestore();
spy2.mockRestore();
});
test('Should call next() with no error when no error with 2 requests', () => {
spy1 = jest.spyOn(service, 'exchangeLongTimeToken').mockImplementation((url) => {
return Promise.resolve(testData.fbLongTimeToken);
});
spy2 = jest.spyOn(service, 'retrieveUserInfo').mockImplementation((url) => {
return Promise.resolve(testData.fbUserInfo);
});
const request = httpMocks.createRequest();
const response = httpMocks.createResponse();
asyncAll(request, response, mockNext);
expect(spy1).toBeCalled();
expect(spy2).toBeCalled();
expect(mockNext).toBeCalled();
expect(mockNext).toBeCalledWith();
expect(mockNext.mock.calls.length).toBe(1);
});
}
The error is like this:
Error: expect(jest.fn()).toBeCalled()
Expected mock function to have been called.
at Object.<anonymous> (tests/backend/unit/fblogin/asyncAll.test.js:39:26)
Which reflects the line:
expect(mockNext).toBeCalled();
Why it doesn't get called?
I read the documents about jest, it says I need to return the promise in order to test the value. But the asyncAll() doesn't return a promise, instead, it consumes a promise, how to deal with this?
You have to notify Jest about the promises you create in the test, have a look at the docs on this topic:
test('Should call next() with no error when no error with 2 requests', async() => {
const p1 = Promise.resolve(testData.fbLongTimeToken);
const p2 = Promise.resolve(testData.fbUserInfo);
spy1 = jest.spyOn(service, 'exchangeLongTimeToken').mockImplementation((url) => {
return p1
});
spy2 = jest.spyOn(service, 'retrieveUserInfo').mockImplementation((url) => {
return p2
});
const request = httpMocks.createRequest();
const response = httpMocks.createResponse();
asyncAll(request, response, mockNext);
await Promise.all([p1,p2])
expect(spy1).toBeCalled();
expect(spy2).toBeCalled();
expect(mockNext).toBeCalled();
expect(mockNext).toBeCalledWith();
expect(mockNext.mock.calls.length).toBe(1);
});

Categories