Using Q.js promise for unit-tests: timeout of 2000ms exceeded - javascript

I'm using Q.js library for simulating async behaviour by using promise
I have a stubed backend api
class ApiStub {
constructor(){
this.deferred = Q.defer();
}
post(url, data) {
if (data) {
this.deferred.resolve(data);
} else {
this.deferred.reject(new Error("Invalid data"));
}
return this.deferred.promise;
}
}
and I'm trying to test it:
before(() => api = new ApiStub());
it("Api test", (done) => {
return api.post({})
.then(value => {
expect(value).to.exist;
done();
}, error => {
done(error);
});
});
but I got an Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
I've tried to set mocha timeout more then 15000ms but it didn't helped

It looks like you have your error handler as part of the the same then with your test case. This means you won't catch any errors thrown by the expect. Try this and see if you get a different error:
it("Api test", (done) => {
return api.post({})
.then(value => {
expect(value).to.exist;
done();
}).catch(error => {
done(error);
});
});

Related

How to throw out of then-catch block?

I am currently figuring out how to throw an Exception out of a then catch block. I want to get into the catch that is inside the errorHandler() function.
const errorHandler = function () {
try {
thisFunctionReturnsAnError().then(response => {
console.log(response);
});
} catch (e) {
console.log(e); //How to trigger this?
}
};
const thisFunctionReturnsAnError = function () {
return3()
.then(value => {
throw new Error('This is the error message.');
})
.catch(err => {
//return Promise.reject('this will get rejected');
throw err;
//this one should somehow got to the catch that is located in the errorHandler() function. How to do this?
//I know that this 'err' will be in the next catch block that is written here. This is not what i want.
});
};
const return3 = async function () {
return 3;
};
errorHandler();
I searched a while on stackoverflow but nothing helped me. I am sure that this question got asked often but I could not find the answer, sorry for that.
EDIT:
added here another version of the code but it still does not work
const errorHandler = async function () {
try {
thisFunctionReturnsAnError()
.then(response => console.log(response))
.catch(err => console.log(err));
} catch (e) {
//console.log(e); //How to trigger this?
}
};
const thisFunctionReturnsAnError = function () {
return3()
.then(value => {
throw new Error('This is the error message.');
})
.catch(err => {
return Promise.reject(`Message is: ${err}`);
});
};
const return3 = async function () {
return 3;
};
errorHandler();
I will get the following error message:
Uncaught (in promise) Message is: Error: This is the error message.
Your code can't be executed, because "thisFunctionReturnsAnError" is not returning an Promise. That means that you can't call "then" on the return value.
thisFunctionReturnsAnError().then(response => { // will not work
Why not always use a promise?
const errorHandler = function () {
thisFunctionReturnsAnError()
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log('errorHandler: Handle the error.');
});
};
const thisFunctionReturnsAnError = function () {
return return_Three()
.then((value) => {
throw new Error('This is the error message.');
})
.catch((err) => {
//return Promise.reject('this will get rejected');
throw err;
//this one should somehow got to the catch that is located in the errorHandler() function. How to do this?
//I know that this 'err' will be in the next catch block that is written here. This is not what i want.
});
};
const return_Three = async function () {
return 3;
};
errorHandler();
/*****JUST ANOTHER SYNTAX*******/
const secondErrorHandler = async function () {
try {
await thisFunctionReturnsAnError();
} catch (error) {
console.log('secondErrorHandler: Handle the error.');
}
};
secondErrorHandler();
You cannot handle promise rejections with synchronous try/catch. Don't use it, use the promise .catch() method like in your thisFunctionReturnsAnError function.
You can handle promise rejections with try/catch when using async/await syntax (which you already do, albeit unnecessarily, in return3):
async function errorHandler() { /*
^^^^^ */
try {
const response = await thisFunctionReturnsAnError();
// ^^^^^
console.log(response);
} catch (e) {
console.log(e); // works
}
}

Handling multiple Promises in Cypress command

What I have
I have command that can request token and it resolves promise on response. Once the promise is being resolved - I put the token into the sessionStorage in SomeTest.js. It works. But I have to put exactly same cy.window()... in every test.
SomeTest.js:
before(()=>{
cy.login().then(res=>
cy.window().then((window) => {
window.sessionStorage.setItem('token', JJSON.stringify(res))
})
);
});
command in commands.js
Cypress.Commands.add('login', function () {
fetcher.emit('tokenRequest',{email:'dummy#mail.com', password:'dummy'});
return new Cypress.Promise((resolve, reject) => {
fetcher.on('onTokenResponse',function(response) {
resolve(response);
});
});
});
What I want
I would like to set sessionStorage in command login() too.
SomeTest.js:
before(()=>{
cy.login();
});
command in commands.js
Cypress.Commands.add('login', function () {
fetcher.emit('tokenRequest',{email:'dummy#mail.com', password:'dummy'});
return new Cypress.Promise((resolve, reject) => {
fetcher.on('onTokenResponse',function(response) {
cy.window().then((window) => {
window.sessionStorage.setItem('token', JSON.stringify(response));
resolve(response);
})
});
});
});
But it throws error: Uncaught CypressError: Cypress detected that you returned a promise from a command while also invoking one or more cy commands in that promise., The cy command you invoked inside the promise was:> cy.window().
I understand the problem is that I am trying to call another promise which is not being returned. But how can I achieve something like this?
I need to
emit tokenRequest
wait for the tokenResponse
get cy.window() and set some sessionStorage item
Edit:
I've found walkaround and I've wrapped login into another function/command which seems to be working but I am not sure how pretty is this solution.
Cypress.Commands.add('loginFull', function () {
return cy.login().then((response)=>{
cy.window().then((window) => {
window.sessionStorage.setItem('token', JSON.stringify(response));
});
});
});
One way that works is to move the cy.window() command to the outermost level of the custom command, so you are not trying to use it within the Promise.
Test
describe('web socket event handler', () => {
let fetcher;
before(() => {
const io = require('socket.io-client');
fetcher = io.connect('http://127.0.0.1:5000');
cy.window().then(win => {
win.sessionStorage.clear(); // ensure clean before test
})
})
Cypress.Commands.add('login', function () {
cy.window().then(win => {
fetcher.emit('tokenRequest',{email:'dummy#mail.com', password:'dummy'});
return new Cypress.Promise((resolve, reject) => {
fetcher.on('onTokenResponse',function(response) {
win.sessionStorage.setItem('token', JSON.stringify(response));
return resolve(response);
});
});
});
});
it('waits for token', () => {
cy.login().then(_ => {
cy.window().then(win => {
console.log(win.sessionStorage.getItem('token'))
// logs "Response: dummy#mail.com"
})
})
})
})
Server
io.on('connection', function(socket){
socket.on('tokenRequest', function(msg){
setTimeout(() => {
io.emit('onTokenResponse', `Response: ${msg.email}`);
}, 2000)
});
});
Ideally would want to make the login command wait on the fetcher event (with timeout?).
I tried this approach Spying on the DOM API, but no luck so far.
Another way is to set a flag within the event listener, and add a command to the queue that waits on it.
Cypress.Commands.add('login', function () {
cy.window().then(win => {
fetcher.emit('tokenRequest',{email:'dummy#mail.com', password:'dummy'});
let flag = { done: false };
fetcher.on('onTokenResponse',function(response) {
win.sessionStorage.setItem('token', JSON.stringify(response));
flag.done = true;
})
cy.wrap(flag).its('done').should('equal', true); // wait here until done
});
});
Now you can call login synchronously
it('waits for token', () => {
cy.login();
cy.window().then(win => {
console.log(win.sessionStorage.getItem('token'))
// logs "Response: dummy#mail.com"
})
})
Cypress.Commands.add('login', function () {
function waitForSocket(callback) {
fetcher.emit('tokenRequest',{email:'dummy#mail.com', password:'dummy'});
fetcher.on('onTokenResponse',function(response) {
callback(response);
});
}
waitForSocket((res) => {
cy.window().then((window) => {
window.sessionStorage.setItem('token', JJSON.stringify(res))
})
})
});
The error points to this: Cypress commands are already promises and they will wait/resolve themselves. So I think there should not be a wrapping promise for the fetcher.
Edit: I think you can wrap the socket tasks in a callback function.

Node.js: Unhandled promise rejection - UnhandledPromiseRejectionWarning

I have a Node.js app. This app has a button that starts a process. The steps in that process return promises. I'm trying to chain these promises together. For some reason, I'm receiving an UnhandledPromiseRejectionWarning. However, in my mind, I've set this up correctly. My code looks like this:
var myButton = document.getElementById('myButton');
if (myButton) {
console.log('here');
myButton.addEventListener('click', executeAction('0'));
}
function executeAction(input) {
let param1 = 'A';
let promise = new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
return promise;
}
function executeStep1() {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
resolve('[something]');
}, 3000);
} catch (ex) {
reject();
}
});
return promise;
}
function executeStep2(p1, p2, p3) {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
console.log('step 2 has executed');
resolve('awesome!')
}, 3000);
} catch (ex) {
reject(ex);
}
});
return promise;
}
I've confirmed that the executeStep2 function runs to completion. I'm basing this in the fact that I can see "step 2 has executed" in the console window. However, to my surprise, I never see "All done" printed in the console window. Instead, I see the UnhandledPromiseRejectionWarning mentioned above. I don't understand two things about this result:
Why am I not seeing "All done" in the console? Shouldn't that function get executed after executeStep2 has resolved?
Where is the rejection coming from? I don't see anything that's rejecting this.
Thank you very much for your help!
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => { // no function(result) here
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
The error is generated from when you call the function(s) that uses promises:
myButton.addEventListener('click', executeAction('0'));
You need to catch rejections there also.
myButton.addEventListener('click', executeAction('0')
.catch((error) => console.log('ERROR', error));
The rejections are caught inside the functions, but not in the outer scope because executeAction('0') returns a promise, or it would but you are using it as a non-async function, so its creating a promise and then returning a pending promise without waiting for it to be resolved. That looks like what's causing the rejection, and its also not handled for the above reason.
This will fix it:
function executeAction(input) {
let param1 = 'A';
return new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
}
You should look into async/await. It can clean this code up significantly.
async function getSomething() {
try {
// throw 'Test error detected.'
return 'test'
}
catch (e) {
throw e
}
}
async function testing() {
try {
const sample = await getSomething()
return sample
} catch (e) {
throw e
}
}
testing()
.then((data) => console.log(data))
.catch((err) => console.log(err))
Run this above example, and then uncomment the throw. Use this pattern for maximum winning. Exceptions should be thrown to the surface level where the functions are called, displayed, used, rendered, etc. Functions should simply throw the error out.
This allows you to use error handling middleware, higher-order functions, listeners, logging, etc.

Paradoxical issue with mocha done() and async await

I have the following test case:
it("should pass the test", async function (done) {
await asyncFunction();
true.should.eq(true);
done();
});
Running it asserts:
Error: Resolution method is overspecified. Specify a callback or
return a Promise; not both.
And if I remove the done(); statement, it asserts:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure
"done()" is called; if returning a Promise, ensure it resolves.
How to solve this paradox?
You need to remove the done parameter as well, not just the call to it. Testing frameworks like Mocha look at the function's parameter list (or at least its arity) to know whether you're using done or similar.
Using Mocha 3.5.3, this works for me (had to change true.should.be(true) to assert.ok(true) as the former threw an error):
const assert = require('assert');
function asyncFunction() {
return new Promise(resolve => {
setTimeout(resolve, 10);
});
}
describe('Container', function() {
describe('Foo', function() {
it("should pass the test", async function () {
await asyncFunction();
assert.ok(true);
});
});
});
But if I add done:
describe('Container', function() {
describe('Foo', function() {
it("should pass the test", async function (done) { // <==== Here
await asyncFunction();
assert.ok(true);
});
});
});
...then I get
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
Removing done as a param from it worked for me! Instead only use expect/should. Example is as follows:
getResponse(unitData, function callBack(unit, error, data){ try {
return request.post(unit, function (err, resp) {
if (!err && resp.statusCode === 200) {
if (resp.body.error) {
return callback(obj, JSON.stringify(resp.body.error), null);
}
return callback(obj, null, resp);
} else {
if (err == null) {
err = { statusCode: resp.statusCode, error: 'Error occured.' };
}
return callback(obj, err, null);
}
});
} catch (err) {
return callback(obj, err, null);
}}
BEFORE:
it('receives successful response', async (done) => {
const getSomeData = await getResponse(unitData, function callBack(unit, error, data){
expect(data.statusCode).to.be.equal(200);
done();
}) })
AFTER (works):
it('receives successful response', async () => {
const getSomeData = await getResponse(unitData, function callBack(unit, error, data){
expect(data.statusCode).to.be.equal(200);
}) })
Sometimes there are cases you need to use async/await + done function in mocha.
For example, in one of my socket.io unit test cases, I have to call db functions with async functions and test socket event handlers which are callback functions:
context("on INIT_CHAT", ()=> {
it("should create a room", async (done) => {
const user = await factory.create("User");
socket.emit("INIT_CHAT", user);
socket.on("JOIN_CHAT", async (roomId) => {
const room = await ChatRoom.findByPk(roomId);
expect(room).to.exist;
// then I need to close a test case here
done();
});
});
});
This will causes the exact same error as in the OP:
Error: Resolution method is overspecified. Specify a callback or return a Promise; not both.
My Workaround:
I just wrapped the entire test code in a promise generator:
context("on INIT_CHAT", ()=> {
it("should create a room", async () => {
const asyncWrapper = () => {
return new Promise(async (resolve) => {
const user = await factory.create("User");
socket.emit("INIT_CHAT", user);
socket.on("JOIN_CHAT", async (roomId) => {
const room = await ChatRoom.findByPk(roomId);
expect(room).to.exist;
resolve(true);
});
});
});
await asyncWrapper();
});
});

Is there a way to get Chai working with asynchronous Mocha tests?

I'm running some asynchronous tests in Mocha using the Browser Runner and I'm trying to use Chai's expect style assertions:
window.expect = chai.expect;
describe('my test', function() {
it('should do something', function (done) {
setTimeout(function () {
expect(true).to.equal(false);
}, 100);
}
}
This doesn't give me the normal failed assertion message, instead I get:
Error: the string "Uncaught AssertionError: expected true to equal false" was thrown, throw an Error :)
at Runner.fail (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3475:11)
at Runner.uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3748:8)
at uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3778:10)
So it's obviously catching the error, it's just not displaying it correctly. Any ideas how to do this? I guess I could just call "done" with an error object but then I lose all the elegance of something like Chai and it becomes very clunky...
Your asynchronous test generates an exception, on failed expect()ations, that cannot be captured by it() because the exception is thrown outside of it()'s scope.
The captured exception that you see displayed is captured using process.on('uncaughtException') under node or using window.onerror() in the browser.
To fix this issue, you need to capture the exception within the asynchronous function called by setTimeout() in order to call done() with the exception as the first parameter. You also need to call done() with no parameter to indicate success, otherwise mocha would report a timeout error because your test function would never have signaled that it was done:
window.expect = chai.expect;
describe( 'my test', function() {
it( 'should do something', function ( done ) {
// done() is provided by it() to indicate asynchronous completion
// call done() with no parameter to indicate that it() is done() and successful
// or with an error to indicate that it() failed
setTimeout( function () {
// Called from the event loop, not it()
// So only the event loop could capture uncaught exceptions from here
try {
expect( true ).to.equal( false );
done(); // success: call done with no parameter to indicate that it() is done()
} catch( e ) {
done( e ); // failure: call done with an error Object to indicate that it() failed
}
}, 100 );
// returns immediately after setting timeout
// so it() can no longer catch exception happening asynchronously
}
}
Doing so on all your test cases is annoying and not DRY so you might want to provide a function to do this for you. Let's call this function check():
function check( done, f ) {
try {
f();
done();
} catch( e ) {
done( e );
}
}
With check() you can now rewrite your asynchronous tests as follows:
window.expect = chai.expect;
describe( 'my test', function() {
it( 'should do something', function( done ) {
setTimeout( function () {
check( done, function() {
expect( true ).to.equal( false );
} );
}, 100 );
}
}
Here are my passing tests for ES6/ES2015 promises and ES7/ES2016 async/await. Hope this provides a nice updated answer for anyone researching this topic:
import { expect } from 'chai'
describe('Mocha', () => {
it('works synchronously', () => {
expect(true).to.equal(true)
})
it('works ansyncronously', done => {
setTimeout(() => {
expect(true).to.equal(true)
done()
}, 4)
})
it('throws errors synchronously', () => {
return true
throw new Error('it works')
})
it('throws errors ansyncronously', done => {
setTimeout(() => {
return done()
done(new Error('it works'))
}, 4)
})
it('uses promises', () => {
var testPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello')
}, 4)
})
testPromise.then(result => {
expect(result).to.equal('Hello')
}, reason => {
throw new Error(reason)
})
})
it('uses es7 async/await', async (done) => {
const testPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello')
}, 4)
})
try {
const result = await testPromise
expect(result).to.equal('Hello')
done()
} catch(err) {
done(err)
}
})
/*
* Higher-order function for use with async/await (last test)
*/
const mochaAsync = fn => {
return async (done) => {
try {
await fn()
done()
} catch (err) {
done(err)
}
}
}
it('uses a higher order function wrap around async', mochaAsync(async () => {
const testPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello')
}, 4)
})
expect(await testPromise).to.equal('Hello')
}))
})
If you like promised, try Chai as Promised + Q, which allow something like this:
doSomethingAsync().should.eventually.equal("foo").notify(done);
I asked the same thing in the Mocha mailing list. They basically told me this : to write asynchronous test with Mocha and Chai :
always start the test with if (err) done(err);
always end the test with done().
It solved my problem, and didn't change a single line of my code in-between (Chai expectations amongst other). The setTimout is not the way to do async tests.
Here's the link to the discussion in the mailing list.
I've published a package that resolves this issue.
First install the check-chai package:
npm install --save check-chai
Then in your tests, use chai.use(checkChai); and then use the chai.check helper function as shown below:
var chai = require('chai');
var dirtyChai = require('dirty-chai');
var checkChai = require('check-chai');
var expect = chai.expect;
chai.use(dirtyChai);
chai.use(checkChai);
describe('test', function() {
it('should do something', function(done) {
// imagine you have some API call here
// and it returns (err, res, body)
var err = null;
var res = {};
var body = {};
chai.check(done, function() {
expect(err).to.be.a('null');
expect(res).to.be.an('object');
expect(body).to.be.an('object');
});
});
});
Per Is there a way to get Chai working with asynchronous Mocha tests? I published this as an NPM package.
Please see https://github.com/niftylettuce/check-chai for more information.
Try chaiAsPromised! Aside from being excellently named, you can use statements like:
expect(asyncToResultingValue()).to.eventually.equal(true)
Can confirm, works very well for Mocha + Chai.
https://github.com/domenic/chai-as-promised
Very much related to and inspired by Jean Vincent's answer, we employ a helper function similar to his check function, but we call it eventually instead (this helps it match up with the naming conventions of chai-as-promised). It returns a function that takes any number of arguments and passes them to the original callback. This helps eliminate an extra nested function block in your tests and allows you to handle any type of async callback. Here it is written in ES2015:
function eventually(done, fn) {
return (...args) => {
try {
fn(...args);
done();
} catch (err) {
done(err);
}
};
};
Example Usage:
describe("my async test", function() {
it("should fail", function(done) {
setTimeout(eventually(done, (param1, param2) => {
assert.equal(param1, "foo"); // this should pass
assert.equal(param2, "bogus"); // this should fail
}), 100, "foo", "bar");
});
});
I know there are many repeat answers and suggested packages to solve this however I haven't seen the simple solutions above offer a concise pattern for the two use cases. I am posting this as a consolidated answer for other who wish to copy-pasta:
event callbacks
function expectEventCallback(done, fn) {
return function() {
try { fn(...arguments); }
catch(error) { return done(error); }
done();
};
}
node style callbacks
function expectNodeCallback(done, fn) {
return function(err, ...args) {
if (err) { return done(err); }
try { fn(...args); }
catch(error) { return done(error); }
done();
};
}
example usage
it('handles event callbacks', function(done) {
something.on('event', expectEventCallback(done, (payload) => {
expect(payload).to.have.propertry('foo');
}));
});
it('handles node callbacks', function(done) {
doSomething(expectNodeCallback(done, (payload) => {
expect(payload).to.have.propertry('foo');
}));
});
Based on this link provided by #richardforrester http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/, describe can use a returned Promise if you omit the done parameter.
Only downside there has to be a Promise there, not any async function (you can wrap it with a Promise, thou). But in this case, code can be extremely reduced.
It takes into account failings from either in the initial funcThatReturnsAPromise function or the expectations:
it('should test Promises', function () { // <= done removed
return testee.funcThatReturnsAPromise({'name': 'value'}) // <= return added
.then(response => expect(response).to.have.property('ok', 1));
});
I solved it extracting try/catch to a function.
function asyncExpect(test, done){
try{
test();
done();
} catch(error){
done(error);
}
}
Then in it() I call:
it('shall update a host', function (done) {
testee.insertHost({_id: 'host_id'})
.then(response => {
asyncExpect(() => {
expect(response).to.have.property('ok', 1);
expect(response).to.have.property('nModified', 1);
}, done);
});
});
It's also debugable.
Timers during tests and async sounds pretty rough. There is a way to do this with a promise based approach.
const sendFormResp = async (obj) => {
const result = await web.chat.postMessage({
text: 'Hello world!',
});
return result
}
This async function uses a Web client (in this case it is Slacks SDK). The SDK takes care of the asynchronous nature of the API call and returns a payload. We can then test the payload within chai by running expect against the object returned in the async promise.
describe("Slack Logic For Working Demo Environment", function (done) {
it("Should return an object", () => {
return sdkLogic.sendFormResp(testModels.workingModel).then(res => {
expect(res).to.be.a("Object");
})
})
});
A simpler approach would be using wait-for-expect library.
const waitForExpect = require("wait-for-expect")
test("it waits for the number to change", async () => {
let numberToChange = 10;
setTimeout(() => {
numberToChange = 100;
}, randomTimeout);
await waitForExpect(() => {
expect(numberToChange).toEqual(100);
});
});
What worked very well for me icm Mocha / Chai was the fakeTimer from Sinon's Library.
Just advance the timer in the test where necessary.
var sinon = require('sinon');
clock = sinon.useFakeTimers();
// Do whatever.
clock.tick( 30000 ); // Advances the JS clock 30 seconds.
Has the added bonus of having the test complete quicker.
You can also use domain module. For example:
var domain = require('domain').create();
domain.run(function()
{
// place you code here
});
domain.on('error',function(error){
// do something with error or simply print it
});

Categories