I'm using Mocha as test runner, Chai for assertion and Sinon.
I'm having trouble using sinon, I have the following function that I want to test in PrestigeQuoteService.js file
find: function (criteria) {
return PrestigeQuote.find(criteria)
.populate('client')
.populate('vehicle')
.populate('quoteLogs');
},
and here is my test case
describe('find()', function () {
describe('prestige quote found', function () {
before(function () {
sandbox = sinon.sandbox.create();
var mockChain = {
populate: function () {
return this;
}
};
sandbox
.stub(PrestigeQuote, 'find').returns(mockChain);
});
it('should return a particular quote', function (done) {
PrestigeQuoteService.find({id: 1}, function (err, result) {
result.should.exist;
done();
});
});
after(function () {
sandbox.restore();
});
});
});
yet I get this error, even thought I have done() and should return value by default.
Error: timeout of 10000ms exceeded. Ensure the done() callback is being called in this test.
Use time out inside it() function .
describe('find()', function () {
describe('prestige quote found', function () {
before(function () {
sandbox = sinon.sandbox.create();
var mockChain = {
populate: function () {
return this;
}
};
sandbox
.stub(PrestigeQuote, 'find').returns(mockChain);
});
it('should return a particular quote', function (done) {
this.timeout(50000);
PrestigeQuoteService.find({id: 1}, function (err, result) {
result.should.exist;
done();
});
});
after(function () {
sandbox.restore();
});
});
});
I solved it by adding a return in mockChain
describe('prestige quote found', function () {
before(function () {
sandbox = sinon.sandbox.create();
var err = null;
var mockChain = {
populate: function () {
return this;
},
return:function () {
return {};
}
};
sandbox
.stub(PrestigeQuote, 'find').returns(mockChain);
});
it('should return a particular prestige quote', function (done) {
PrestigeQuoteService.find({id: 1}, function (result) {
result.should.exist;
});
done();
});
after(function () {
sandbox.restore();
});
});
Related
I have written some tests in my Node project using Mocha and Chai. In one of my tests I create an agenda.js job, and then save it to the database. Then I retrieve that document from my MongoDB database and run some checks on it. After numerous configurations, I've found a construction for the test that works. But to get this to work I had to add a setTimeout() within the first it block, because otherwise the it checks start running before the document is retrieved from the database.
While the following construction works, I'd like to know what would be a better way of doing this. It seems to me the whole point of the before block in Mocha is to ensure that whatever work is defined within it is done BEFORE the it checks run. That doesn't seem to be happening in my case - hence the need for the setTimeout(). So how can I accomplish that without resorting to using the `setTimeout()?:
const assert = require("chai").assert;
const expect = require("chai").expect;
const chai = require("chai");
chai.use(require("chai-datetime"));
const Agenda = require('agenda');
const config = require('./../../configuration');
const url = config.get('MONGO_URL');
const dbName = config.get('MONGO_DATABASE');
const collection = config.get('MONGO_COLLECTION');
const createAgendaJob = require('./../../lib/agenda-jobs/contact-firstname-to-proper-case');
const MongoClient = require('mongodb').MongoClient;
const client = new MongoClient(url);
describe("Contact FirstName to Proper Case", async function () {
const jobName = "Contact FirstName To Proper Case";
const testDate = new Date(2019, 01, 01);
let result;
let agenda;
this.timeout(10000);
before(async function () {
const connectionOpts = {
db: {
address: `${url}/${dbName}`,
collection
}
};
agenda = new Agenda(connectionOpts);
await new Promise(resolve => agenda.once('ready', resolve));
await createAgendaJob(agenda);
});
describe("Check Contact FirstName To ProperCase Found Job", async function () {
let result;
before(async function () {
await client.connect(async function (err) {
assert.equal(null, err);
const db = await client.db(dbName);
result = await db.collection("jobs").findOne({
"name": jobName
});
client.close();
});
});
it("should have a property 'name'", async function () {
await new Promise(resolve => setTimeout(resolve, 1000)); // Here is the setTimout()
expect(result).to.have.property("name");
});
it("should have a 'name' of 'Contact FirstName To Proper Case'", async function () {
expect(result.name).to.equal("Contact FirstName To Proper Case");
});
it("should have a property 'type'", function () {
expect(result).to.have.property("type");
});
it("should have a 'type' of 'normal'", function () {
expect(result.type).to.equal("normal");
});
it("should have a property 'repeatTimezone'", function () {
expect(result).to.have.property("repeatTimezone");
});
it("should have a property 'repeatInterval'", function () {
expect(result).to.have.property("repeatInterval");
});
it("should have a property 'lastModifiedBy'", function () {
expect(result).to.have.property("lastModifiedBy");
});
it("should have a property 'nextRunAt'", function () {
expect(result).to.have.property("nextRunAt");
});
it("should return a date for the 'nextRunAt' property", function () {
assert.typeOf(result.nextRunAt, "date");
});
it("should 'nextRunAt' to be a date after test date", function () {
expect(result.nextRunAt).to.afterDate(testDate);
});
});
});
The async function inside before may be resolving early. In that case I would wrap it in a new Promise and resolve when I am sure all async code has resolved to completion.
//...
before(function () {
return new Promise((resolve, reject) => {
client.connect(async function (err) {
if(err) return reject(err);
try {
const db = await client.db(dbName);
result = await db.collection("jobs").findOne({
"name": jobName
});
client.close();
} catch(err){
return reject(err);
}
return resolve();
});
});
})
//...
Alternatively, call the done callback, passing in a truthy value if there is an error.
//...
before(function (done) {
client.connect(async function (err) {
if(err) return done(err);
try {
const db = await client.db(dbName);
result = await db.collection("jobs").findOne({
"name": jobName
});
client.close();
} catch(err){
return done(err);
}
done();
});
})
//...
mocha error: timeout of 2000ms exceeded, for async test and hooks ensure the done() callback is being called in this test. if returning a promise, ensure it resolves.
This is what the error i am getting while trying to get response,
This is my index.js file where i export function
exports.info = function(callback) {
var https = require('https');
var options = {
host: 'api.github.com',
path: '/repos/sayanee/build-podcast',
method: 'GET',
headers: { 'User-Agent': 'sayanee' } };
var str = '';
https.request(options, function(response) {
response.on('data', function(data) {
str += data;
});
response.on('end', function() {
callback(JSON.parse(str));
})
response.on('error', function(error) {
console.log(error);
callback();
})
}) .end();
}
This is my indexfile where i describe the test cases
function asyncFunction() {
return new Promise(resolve => {
setTimeout(resolve, 5000);
});
}
describe('Github info', function() {
it.only('returns repo info from github', async function() {
//this.timeout(5000);
await asyncFunction();
word.info(function(reply) {
console.log("er")
expect(reply.language).to.equal('JavaScript');
expect(reply.watchers).to.equal(157);
console.log('RECEIVED');
});
console.log('HELLO'); })
});
Mocha support async test also by passing done callback as param to it that you need to call at test end
describe("Github info", function () {
it.only("returns repo info from github", function (done) {
// set long timeout to be sure word.info finish
this.timeout(5000);
word.info(function (reply) {
console.log("er");
expect(reply.language).to.equal("JavaScript");
expect(reply.watchers).to.equal(157);
console.log("RECEIVED");
// call done at end
done();
});
console.log("HELLO");
});
});
The response is in your question. Mocha is setted up to timeout after 2 second.
Either you makes your request to finish within 2000ms
Either you increase the Mocha timeout, example :
mocha -t 300000
EDIT :
You cannot use async/await mixed up with callbacks
// Wrap your function into a promise
wordInfoPromise() {
return new Promise((resolve, reject) => {
word.info((ret) => {
if (!ret) return reject();
return resolve(ret);
});
});
}
it('returns repo info from github', async function() {
//this.timeout(5000);
await asyncFunction();
const reply = await wordInfoPromise();
console.log("er")
expect(reply.language).to.equal('JavaScript');
expect(reply.watchers).to.equal(157);
console.log('RECEIVED');
console.log('HELLO'); })
});
EDIT 2 :
const req = https.request(options, (res) => {
res.on('data', (d) => {
str += data;
});
res.on('end', () => {
resolve(str);
});
});
req.on('error', (e) => {
reject();
});
req.end();
My unit test is:
describe.only("Validator Service Tests", function () {
let request
before((done) => {
request = sinon.stub()
done()
})
beforeEach(() => {
process.env.API_URL = "http://test"
})
it('Should return with no errors if the file matches the schema', () => {
const updateStatusSpy = sinon.spy(FileLib, 'updateStatus')
request.yields({message: 'ok'})
return ValidatorService.handleMessage({
file: 'test'
})
.then((response) => {
assert()
console.log(response)
sinon.assert.calledOnce(updateStatusSpy)
assert(response, 'f')
})
})
})
The problem is my handleMessage function, which looks like:
exports.handleMessage = (message, done) => {
return stuff()
.then((result) => {
console.log('result', result)
if(result) {
return FileLib.updateStatus(fileId, 'valid')
}
return FileLib.updateStatus(fileId, 'invalid')
})
.then(done)
}
And my updateStatus function:
exports.updateStatus = function(fileId, status) {
console.log(fileId, status)
return request.put({
uri: `${process.env.API_URL}/stuff/${fileId}`,
body: {
status: status
}
})
}
My actual request call is buried so deep in, how can I stub it out when testing?
I'm not sure I completely understand your question, but if you are just trying to stub put, try something like this:
let stub;
beforeEach(() => {
putStub = sinon.stub(request, 'put').resolves('some_val_or_object'); //or yields or callsFake, depending on what you're using
});
it('should call request with put', async () => {
await //call your code
expect(putStub.called).to.be.true;
expect(putStub.calledWith(whatever_you_want_to_check)).to.be.true;
});
I've got a simple function such as;
module.exports = {
fetchUser:function(myUserId) {
return new Promise((resolve, reject) => {
this.getUser(myUserId)
.then(user => {
// do logic // then return user
return user;
})
.then(resolve)
.catch(err => {
// whoops there has been an error
let error = { error: 'My Error' };
reject(error);
});
});
}
};
I want to unit test both the resolve and reject result.
A simple chai test would be;
var expect = require('chai').expect;
var user = require('./user');
describe('User module', function() {
it('test fetchUser', function() {
let _user = user.fetchUser('abc123');
return _user
.then(user => {
expect(data).to.be.an('object');
});
});
Using sinon or another library, how can I for the fetchUser function to throw that reject error?
With Mocha, Chai and Sinon it can be implemented with stubbed method getUser.
const User = require("./fetchUserModule");
describe('User module', () => {
beforeEach(() => User.getUser = sinon.stub());
afterEach(() => User.getUser.reset());
it('returns user if `getUser` returns data', () => {
const user = {name: 'John'};
User.getUser.withArgs("abc123").returns(Promise.resolve(user));
return User.fetchUser("abc123").then(result => {
expect(result).to.equal(user)
}).catch(error => {
expect(error).to.be.undefined;
})
});
it('throws error if `getUser` is rejected', () => {
User.getUser.withArgs("abc123").returns(Promise.reject());
return User.fetchUser("abc123").then(result => {
expect(result).to.be.undefined;
}).catch(err => {
expect(err).to.eql({error: 'My Error'})
})
});
});
Start with anything in your "logic" that can throw an error.
If not you would need to stub this.getUser to reject or throw an error instead of returning data. sinon-as-promised patches sinon.stub to include the .resolves and .rejects promise helpers.
const sinon = require('sinon')
require('sinon-as-promised')
Setup the stub for the failure tests.
before(function(){
sinon.stub(user, 'getUser').rejects(new Error('whatever'))
})
after(function(){
user.getUser.restore()
})
Then either catch the .fetchUser error or use chai-as-promised for some sugar.
it('test fetchUser', function() {
return user.fetchUser('abc123')
.then(()=> expect.fail('fetchUser should be rejected'))
.catch(err => {
expect(err.message).to.eql('whatever')
})
})
it('test fetchUser', function() {
return expect(user.fetchUser('abc123')).to.be.rejectedWith(Error)
})
or async if you live in the new world
it('test fetchUser', async function() {
try {
await user.fetchUser('abc123')
expect.fail('fetchUser should be rejected'))
} catch(err) {
expect(err.message).to.eql('whatever')
}
})
As a side note, you don't need to wrap something that already returns a promise in new Promise and be careful about losing error information when chaining multiple .catch handlers.
fetchUser: function (myUserId) {
return this.getUser(myUserId)
.then(user => {
//logic
return user
})
.catch(err => {
let error = new Error('My Error')
error.original = err
reject(error)
});
}
I have the following nodejs function using Sequelize:
var processDatabase = function (dbConnection, schema, recordsets) {
var myLogTable = dbConnection.define(schema.tableName, schema.myLogSchema, schema.myLogSchemaIndex);
myLogTable.sync({
force: false,
freezeTableName: true,
logging: console.log
}).then(function () {
console.log('Table synced...');
for (k = 0; k < recordsets.length; k++) {
var query = "Some query";
dbConnection.query(
query, {
type: dbConnection.QueryTypes.SELECT
}
)
.then(function (results) {
console.log('MYSQL Selection Done');
})
.catch(function (err) {
console.log('MYSQL Error: ' + err.message);
});
}
}).catch(function (err) {
console.log('MYSQL Sync Error: ' + err.message);
});
};
I am new to mocking and do not especially know how to test the catch part.
This is my unit test which I can come up with, but I do not know how a call to sync can go to the catch part:
describe('when call processDatabase', function () {
it('should process successfully when sync fails', function (done) {
seqConnection.define = function (tableName, schema, schemaIndex) {
return mockMyLogModel;
};
processProfilesNotMapped(seqConnection, {
tableName: 'SomeTable',
myLogSchema: myLogSchema,
myLogSchemaIndex: myLogSchemaIndex
}, []);
done();
})
});
How would I write my mocking so that I can test both catch and also then so that they can be covered?
You need to defer an exception in your mock, since "sync" is using promises. You can use q library or any other. This way when you execute the sync function it will go to the catch section
Example using q:
describe('when call processDatabase', function () {
it('should process successfully when sync fails', function (done) {
seqConnection.define = function (tableName, schema, schemaIndex) {
const mock = {
sync: function(){
const deferred = q.defer();
deferred.reject(new Error('Some error'));
return deferred.promise;
}
}
return mock;
};
expect(
function(){
cmdManager.execute("getProfileDummy","hosar#gmail.com")
}
).to.throw(Error);
processProfilesNotMapped(seqConnection, {
tableName: 'SomeTable',
myLogSchema: myLogSchema,
myLogSchemaIndex: myLogSchemaIndex
}, []);
done();
})
});