Nested promises - Mocha - Exceeded timeout - javascript

I have got a test failing because of a timeout using Mocha.
I do call the "done()" function but it does not seem to work for some reason.
My test currently looks like this:
var express = require('express');
var expect = require('chai').expect;
var mocha = require('mocha');
var calendar = require('./../Server/calendarDatabase');
describe("Calendar", function () {
describe("Database", function () {
it("should get stuff from the database", function (done) {
return calendar.Connect()
.then(function () {
return calendar.getAll();
})
.then(function (returnValue) {
expect(returnValue.count).to.equal(5); //This should fail, my database records are 20+
done();
});
});
});
});
Where my calendar.Connect() and calendar.getAll() are both promises:
var express = require('express');
var sql = require('mssql');
var config = require('./../config');
var calendarDbConnection = {};
calendarDbConnection.Connect = function() {
return sql.connect(config.mssql);
}
calendarDbConnection.getAll = function () {
var promise = new sql.Request()
.query('select * from CalendarTable')
.catch(function (err) {
console.log(err.message);
});
return promise;
}
module.exports = calendarDbConnection;
However while running my test, I get following output:
When I call the done() after my last then(), the function gets resolved but the outcome of my test does not. The number of lines I get from the database are over 20 and I check if they are equal to 5. So, my test should fail, but it does not.
//previous code
.then(function (returnValue) {
expect(returnValue.count).to.equal(5); //This should fail, my database records are 20+
});
done();
//...
So this last outcome passes my test, while it should not.
What am I missing here? I am calling the callback function but then my expected outcome is not correct.

Since You are returning a Promise from the test, you should not pass done as an argument:
Alternately, instead of using the done() callback, you may return a Promise. This is useful if the APIs you are testing return promises instead of taking callbacks.
Although you can pass done to catch call as stated above, it seems more convenient to get rid of done and just return a promise.
it("should get stuff from the database", function () {
return calendar.Connect() // returns Promise, so no `done`
.then(function () {
return calendar.getAll();
})
.then(function (returnValue) {
expect(returnValue.count).to.equal(5);
});
});

Only pass done in the catch.
then(....) { ... done(); }.catch(done);

Related

How to run a function based on variable value which is dependent on other function in specs jasmine?

Requirement is to run testRunner function based on variable 'data' which is dependent on function 'getSteps'
My code as below
function getSteps()
it('test', async function () {
testStepsArray = await excel.getColValue('smoke.xlsx', 'Sheet1', 'A')
return testStepsArray
});
}
function testRunner(count) { **//i want to run this function based on value of data variable below**
it('test', async function () {
for(var j=0;j<count;j++)
{
...
}
});
}
var data = getSteps();
console.log(data.length+"LENGTH") **//returns Cannot read property 'length' of undefined.**
for(var i=1; i<= data.length;i+=count)
{
...
testRunner(i)
}
i think last block is not waiting for results of getSteps. Please suggest.
Update:
After inputs, I modified as below and i see difference. I now can get data values properly but execution fails when there is a function wrapped around spec but works with regular function
function testRunner(count) {
it('test', async function () {
for(var j=0;j<count;j++)
{
...
}
});
}
function test(){
...
}
let promise = new Promise((resolve,reject)=>{
it('test', async function () {
testStepsArray = await excel.getColValue('smoke.xlsx', 'Sheet1', 'A')
resolve(testStepsArray)
});
}
})
promise.then((data)=>{
console.log(data.length+"LENGTH")
for(var i=1; i<= data.length;i+=count)
{
testRunner(i) //fails if function is like testrunner() - function wrraped around specs
test() //works if function is like test() - regular function
}
})
Update 2:Promise rejection Error logs
[32m. [0mError: 'it' should only be used in 'describe' function
at ensureIsNotNested (\nodejs\node_modules\jasmine_3.5.0\node_modules\jasmine-core\lib\jasmine-core\jasmine.js:1786:15)
at Env.it (\nodejs\node_modules\jasmine_3.5.0\node_modules\jasmine-core\lib\jasmine-core\jasmine.js:1933:7)
at it (nodejs\node_modules\jasmine_3.5.0\node_modules\jasmine-core\lib\jasmine-core\jasmine.js:6576:21)
at testRunner (webapps\mysamples\testRunner.js:66:4)
at webapps\mysamples\testRunner.js:59:4
at processTicksAndRejections (internal/process/task_queues.js:97:5)
What about Promise? It waits until your function resolve "the data"
let promise = new Promise((resolve,reject)=>{
it('test', async function () {
testStepsArray = await excel.getColValue('smoke.xlsx', 'Sheet1', 'A')
resolve(testStepsArray)
});
}
})
promise.then((data)=>{
console.log(data.length+"LENGTH")
for(var i=1; i<= data.length;i+=count)
{
...
testRunner(i)
}
})
Read more about promise
Update
A rejected promise means your execution of code is not work as expected.
i.e. A website GET request.
let promise = new Promise((resolve,reject)=>{
$.get(some_website, function(data, status, error){
if(status==200){
//Get request success, then return the data from website
resolve(data);
} else {
//Get request fail, return error message
reject(error);
}
});
})
I guess you are not successfully running excel.getColValue('smoke.xlsx', 'Sheet1', 'A')
try to add the reject(something) for fail case.
Then in your consuming part, add this:
promise.then((data)=>{
//blah blah blah
}).catch((err)=>{
console.log(err) //read what's up with your operation
})
Or if you find that your promise got stuck in the for loop(did not wait the for loop complete, please use promise.all()
References

"Ensure the done() callback is being called in this test" when testing a function that returns a promise

I wrote a very simple function that returns a promise. When the promise resolves it returns all the trip objects from the database. When I console.log the result in a then function, I see all the objects, so I know the function works. But when I try to test it, I get this "timeout" exception. I wrote another function that returns a simple promise object with omitting all mongoose and database connection objects and it threw the same error. I think it is about resolving promises in mocha an not about mongoose or database connection.
Can anyone help me find what is wrong here or how should i keep investigating the problem?
Thanks in advance.
in the processRoutes.js:
var mongoose = require('mongoose');
require('../models/Trip');
mongoose.connect('mongodb://localhost:27017/gtfs-dev');
var mongodb = mongoose.connection;
var Trip = mongodb.model('Trip');
var findTrips = function () {
var query = Trip.find({},{"trip_id":1, "_id":0});
var promise = query.exec();
return promise;
}
processRoutes.spec.js:
...
mongoose.Promise = global.Promise;
var processroutes = require('./processRoutes');
describe('ProcessRoute', function () {
it('dependencies should exist', function () {
expect(processroutes).to.be.not.undefined;
});
describe('#findTrips()', function(){
it('should exist and be a function', function(){
expect(processroutes.findTrips).to.be.not.null;
expect(processroutes.findTrips).to.be.a('function');
});
it('should return a promise', function(){
var promise = processroutes.findTrips();
expect(promise).to.be.a('promise');
});
it('should resolve to an array', function(){
return processroutes.findTrips().then(function(trips){
expect(trips).to.be.instanceof(Array);
});
});
});
});
The result of the tests is like this:
ProcessRoute
✓ dependencies should exist
#findTrips()
✓ should exist and be a function
✓ should return a promise
1) should resolve to an array
3 passing (5s)
1 failing
1) ProcessRoute #findTrips() should resolve to an array:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

How to propagate callback promise value on Model.find( obj , callback)?

Ok, I'm fighting hard this problem. I've spent a lot of time on the past week trying to figure out how to make this work. I've learned promises and made some cool stuff - and I'm love with it. But, I can't make this work.
I'm using Mongoose Model.find() method. This methods receives two arguments: an object that will be used to the query and a callback function that will receive (error, data) objects. I'm calling .find and passing findUser function.
UserModel.find(userObj, findUser)
.then(userFound, createUser);
Inside findUser, I'm creating a Promise and resolving or rejecting it.
var findUser = function(err, data) {
var p1 = new Promise( function (resolve, reject) {
if (data.length) {
console.log('Resolved on findUser');
resolve();
} else {
console.log('Rejected on findUser');
reject();
}
});
};
But, whatever happens on findUser, the success callback is always called. On the console, I can see something like this:
Rejected on findUser
Resolved on find
var userFound = function () {
console.log('Resolved on find');
};
var createUser = function () {
console.log('Rejected on find');
}
How could I propagate the promise value from findUser to .find?
You can try something like this. All changes in your code are commented.
// Remove err argument
var findUser = function(data) {
// Instead of creating a new Promise,
// we return a value to resolve
// or throw a value to reject.
if (data.length) {
console.log('Resolved on findUser');
// Resolve this Promise with data.
return data;
} else {
var reason = 'Rejected on findUser';
console.log(reason);
// Reject this Promise with reason.
throw reason;
}
};
// Function gets data passed to it.
var userFound = function(data) {
console.log('Resolved on find');
};
// Function gets reason passed to it.
var createUser = function(reason) {
console.log('Rejected on find');
};
// Promises will chain and propagate errors as necessary.
UserModel.find(userObj)
.then(findUser)
.then(userFound)
.catch(createUser);

How to use async mocha before() initialization without setTimeout?

I am using Mocha to test out some database queries I created. I need my before block to create the appropriate foreign keys so the unit tests can focus on testing out the raw create/delete functionality of my Node ORM.
My problem is that I am inserting db entries in my before block and the unit tests are running before the before block is finished executing.
I read that promises should be used to deal with this kind of thing, so I refactored my codebase to use promises but I still can't get around the fact that I need a setTimeout.
How can I perform async before block without needing to wrap my first it block in a setTimeout?
var chai = require('chai');
var expect = chai.expect;
var db = require('../server/db/config');
var models = require('../server/db/models');
describe('scoring', function() {
var testMessage = {
x: 37.783599,
y: -122.408974,
z: 69,
message: 'Brooks was here'
};
var messageId = 1;
before(function() {
var token = '' + Math.random();
models.createUser(token).then(function() {
testMessage.userToken = token;
models.insert(testMessage)
});
});
it('should have votes created in db when createVote is called', function(done) {
setTimeout(function(done) {
models.createVote(messageId, token, function(err, res) {
expect(res.insertId).to.be.a('number');
done();
});
}.bind(this, done), 1000);
});
});
You could do it like joews suggests. However, Mocha supports using promises for synchronization. You have to return your promise:
before(function() {
var token = '' + Math.random();
return models.createUser(token).then(function() {
testMessage.userToken = token;
models.insert(testMessage)
});
});
Mocha won't continue to the test until it is able to execute .then on the promise returned by your code.
The function you pass to before, like the other Mocha API methods, can accept a done callback. You should call this when your before actions have completed:
before(function(done) {
var token = '' + Math.random();
models.createUser(token).then(function() {
testMessage.userToken = token;
return models.insert(testMessage)
}).then(function() {
done();
})
});
Mocha docs - asynchronous code

How does one stub promise with sinon?

I have a data service with following function
function getInsureds(searchCriteria) {
var deferred = $q.defer();
insuredsSearch.get(searchCriteria,
function (insureds) {
deferred.resolve(insureds);
},
function (response) {
deferred.reject(response);
});
return deferred.promise;
}
I want to test following function:
function search ()
{
dataService
.getInsureds(vm.searchCriteria)
.then(function (response) {
vm.searchCompleted = true;
if (response.insureds.length > 100) {
vm.searchResults = response.insureds.slice(0, 99);
} else {
vm.searchResults = response.insureds;
}
});
}
How would I stub the promise so that when I call getInsureds it would resolve the promise and return me the results immediately. I started like this (jasmine test), but I am stuck, as I don't know how to resolve the promise and pass in arguments needed.
it("search returns over 100 results searchResults should contain only 100 records ", function () {
var results103 = new Array();
for (var i = 0; i < 103; i++) {
results103.push(i);
}
var fakeSearchForm = { $valid: true };
var isSearchValidStub = sinon.stub(sut, "isSearchCriteriaValid").returns(true);
var deferred = $q.defer();
var promise = deferred.promise;
var dsStub = sinon.stub(inSearchDataSvc, "getInsureds").returns(promise);
var resolveStub = sinon.stub(deferred, "resolve");
//how do i call resolve and pass in results103
sut.performSearch(fakeSearchForm);
sinon.assert.calledOnce(isSearchValidStub);
sinon.assert.calledOnce(dsStub);
sinon.assert.called(resolveStub);
expect(sut.searchResults.length).toBe(100);
});
At current sinon version v2.3.1, you can use stub.resolves(value) and stub.rejects(value) function
For example, you can stub myClass.myFunction with following code
sinon.stub(myClass, 'myFunction').resolves('the value you want to return');
or
sinon.stub(myClass, 'myFunction').rejects('the error information you want to return');
You just have to resolve the promise before you call the search function. This way your stub will return a resolved promise and then will be called immediately. So instead of
var resolveStub = sinon.stub(deferred, "resolve");
you will resolve the deferred with your fake response data
deferred.resolve({insureds: results103})
Also you can do something like this:
import sinon from 'sinon';
const sandbox = sinon.sandbox.create();
const promiseResolved = () => sandbox.stub().returns(Promise.resolve('resolved'));
const promiseRejected = () => sandbox.stub().returns(Promise.reject('rejected'));
const x = (promise) => {
return promise()
.then((result) => console.log('result', result))
.catch((error) => console.log('error', error))
}
x(promiseResolved); // resolved
x(promiseRejected); // rejected
I had a similar situation and the accepted answer and comments were not working, but along with this question they helped me solve this in the following way. I hope it is helpful for somebody.
var Promise = require('bluebird');
var deferred = Promise.defer();
stub = sinon.stub(deferred, 'resolve').returns(deferred.promise);
deferred.resolve({data: data});
// or
deferred.reject(new Error('fake error'));
There's one more alternative I found. Much pain free than other methods.
You can use this npm package: sinon-stub-promise.
It abstracts much of the details, so that you don't have to invent the wheel again. Helped my write my tests after struggling to simulate a promise for long.
Hope it helps!

Categories

Resources