I want to check if an async function throws using assert.throws from the native assert module.
I tried with
const test = async () => await aPromise();
assert.throws(test); // AssertionError: Missing expected exception..
It (obviously?) doesn't work because the function exits before the Promise is resolved.
Yet I found this question where the same thing is attained using callbacks.
Any suggestion?
(I'm transpiling to Node.js native generators using Babel.)
node 10 and newer
Since Node.js v10.0, there is assert.rejects which does just that.
Older versions of node
async functions never throw - they return promises that might be rejected.
You cannot use assert.throws with them. You need to write your own asynchronous assertion:
async function assertThrowsAsynchronously(test, error) {
try {
await test();
} catch(e) {
if (!error || e instanceof error)
return "everything is fine";
}
throw new AssertionError("Missing rejection" + (error ? " with "+error.name : ""));
}
and use it like
return assertThrowsAsynchronously(aPromise);
in an asynchronous test case.
Based on Bergi answer I've suggest more universal solution that utilizes original assert.throws for error messages:
import assert from 'assert';
async function assertThrowsAsync(fn, regExp) {
let f = () => {};
try {
await fn();
} catch(e) {
f = () => {throw e};
} finally {
assert.throws(f, regExp);
}
}
Usage:
it('should throw', async function () {
await assertThrowsAsync(async () => await asyncTask(), /Error/);
});
The answers given work, but I came across this issue today and came up with another solution, that I think is a little simpler.
// Code being tested
async function thisFunctionThrows() {
throw new Error('Bad response')
}
// In your test.
try {
await thisFunctionThrows()
assert.equal(1 == 0) // Never gets run. But if it does you know it didn't throw.
} catch (e) {
assert(e.message.includes('Bad response'))
}
Since the question is still getting attention, I'd like to sum up the two best solutions, especially to highlight the new standard method.
Node v10+
There's a dedicated method in the assert library, assert.rejects.
For older versions of Node
A fill from vitalets answer:
import assert from 'assert';
async function assertThrowsAsync(fn, regExp) {
let f = () => {};
try {
await fn();
} catch(e) {
f = () => {throw e};
} finally {
assert.throws(f, regExp);
}
}
You are going to want to use, assert.rejects() which is new in Node.js version 10.
At the high level, instead of assert.throws, we want something like assert.rejects, hopefully you can take this and run with it:
const assertRejects = (fn, options) => {
return Promise.resolve(fn()).catch(e => {
return {
exception: e,
result: 'OK'
}
})
.then(v => {
if (!(v && v.result === 'OK')) {
return Promise.reject('Missing exception.');
}
if (!options) {
return;
}
if (options.message) {
// check options
}
console.log('here we check options');
});
};
it('should save with error', async () => {
// should be an error because of duplication of unique document (see indexes in the model)
return await assertRejects(async () => {
patientSubscriber = await PatientSubscriber.create({
isSubscribed: true,
patient: patient._id,
subscriber: user._id
});
}, {
message: /PatientSubscriber validation failed/
});
});
Related
I'm fairly new to JS development and I've recently discovered the concept of DRY (Don't Repeat Yourself), which has helped me clean up my code a lot.
I have the following type of issue in a few places throughout my project and I'm struggling to think of a way to improve it whilst maintaining the principles of readability and of not repeating code.
if (something) {
doPromise().then(() => {
doSomething()
}).catch(e => {
doThisInstead()
})
} else {
doThisInstead()
}
The crux of it is that I need to execute doThisInstead() or whatever function / in-line code is there whenever either the if statement goes to the else block, or when the promise goes to the catch block, and in this particular instance, I have no way of knowing that the promise will go to the catch block before it is attempted.
Writing code like this can quickly become messy, so I'd appreciate any tips. Many thanks!
You might be looking for if-else flow in promise (bluebird), just with catch instead of then:
(something
? doPromise().then(() => {
doSomething()
})
: Promise.reject()
).catch(e => {
doThisInstead()
})
Written with async/await, it would be
try {
if (!something)
throw new Error("something is wrong")
await doPromise();
await doSomething();
} catch(e) {
await doThisInstead();
}
An alternative that does not rely as much on exceptions would be
if (!something || await doPromise().then(doSomething).then(() => false, () => true)) {
doThisInstead();
}
If you use the _async / await _ syntax, you can await doPromise(), then run doThisInstead() if either something is falsey or an error occurred, this means only one call to doThisInstead() in your code.
This example will cause doPromise() to fail 50% of the time.
let something = true;
// Fail 50% of the time
function doPromise() {
return new Promise((resolve, reject) => setTimeout(Math.random() <= 0.5 ? resolve: () => reject(new Error("Some error")), 100));
}
function doSomething() {
console.log("doSomething()");
}
function doThisInstead() {
console.log("doThisInstead()");
}
async function test() {
errorOccurred = false;
if (something) {
try {
await doPromise();
doSomething();
} catch (e) {
errorOccurred = true;
}
console.log("doPromise: " + (errorOccurred ? "error occurred." : "ran successfully"));
}
// Run doThisInstead() if either an error occurred or something is falsey
if (!something || errorOccurred) {
doThisInstead();
}
}
test()
This can be solved with a Promise, using the following code:
function hypotheticalFunction() {
const doSomething = () => {
// stuff
}
const doThisInstead = () => {
// stuff
}
const doSomethingHandler = () => {
return new Promise((resolve,reject) => {
if (something) {
doPromise().then(() => {
doSomething();
resolve();
}).catch(() => {
reject();
})
} else {
reject();
}
})
}
doSomethingHandler().catch(doThisInstead);
}
hypotheticalFunction();
I'm tryng to upgrade this code for a better maintenance, this code uploads two images to a server, i know it's possible to get rid of those .catch, by applying async await functions, and try catch blocks, but it's pretty confusing for me, any help will be apreciated.
this._uploadService.makeFileRequest(Global.url + "/upload-image1/" + response.product._id, [], this.filesToUpload1, 'image')
.then((result: Product) => {
this.filesToUpload1 = null;
this._uploadService.makeFileRequest(Global.url + "/upload-image/" + response.product._id, [], this.filesToUpload, 'image')
.then((result: Product) => {
this.filesToUpload = null;
setTimeout( () => this._router.navigate(['/detail', this.saveProduct._id]), 800 );
})
.catch(err => {
console.log(err);
this._router.navigate(['/detail', this.saveProduct._id]);
})
})
.catch(err => {
console.log(err);
this._router.navigate(['/detail', this.saveProduct._id]);
})
I suggest using a pen and paper to draw a block diagram for the logic involved, i.e. which api gets called first, with what kind of data, then which api comes afterwards; also include any logical conditionals through branching.
After that, you should attempt to write something like
const aggregateFunction = async() => {
try {
const someResponse = await callFirstApi(); // return response
await callSecondApi(someResponse); // use the response of the first api for the second api
if (someConditional) {
await callThirdApi(); // response not returned (i.e. when not required)
}
} catch (error) { // catch all errors from all await if they're not within another try-catch
console.log(error);
}
}
This pattern should eliminate all then and catch blocks. If you need more specific error handling for calling say a specific api, wrap function call inside another try-catch block, but everything should still be within the outer try-catch so that all errors will be caught regardless.
this._uploadService.makeFileRequest = function(){
return new Promise(resolve => {
// do logic of file request
resolve(true);
})
}
var waitForTime = function() {
return new Promise(resolve => {
setTimeout( () => {
this._router.navigate(['/detail', this.saveProduct._id]),
resolve(true)
}, 800 );
})
}
var f = async function(){
try {
await this._uploadService.makeFileRequest(Global.url + "/upload-image1/" + response.product._id, [], this.filesToUpload1, 'image');
await this.fileToUpload1 = null;
await this._uploadService.makeFileRequest(Global.url + "/upload-image/" + response.product._id, [], this.filesToUpload, 'image')
await this.fileToUpload = null;
await waitForTime();
}
catch(e) {
// error logic
}
}
if (this.filesToUpload1 && this.filesToUpload) {
f()
}
this might be another cleaner approach with async,await and promise
Hi I am write a unit test case of this function. When I run this function from the unit test case then it covers all statements but setInterval complete lines are not covered.
Does anyone know how to cover it in javascript? I am using mocha.
const open = async function (config) {
...... set of lines..
let retryIn = setInterval(async () => {
try {
client = await connect(config);
clearInterval(retryIn);
return client;
} catch (err) {
//error
}
}, 20000);
};
I am simply calling it like this
it("###test", async () => {
open(config);
});
});
First of all, you should never use setInterval in the case where you want to retry a task that has failed. You should use setTimeout instead.
Besides that, you cannot return a value from a classical callback base function like setInterval or setTimeout. So in its current form, the promise returned when calling open will be resolved before any connection is made.
With await/async you can create a clean and simple setup for such a situation:
function wait(seconds) {
return new Promise((resolve, _) => setTimeout(resolve, seconds))
}
async function connect() {
throw new Error('failed')
}
async function open(config) {
let client;
while (client === undefined /* || retries > maxRetries*/ ) {
try {
client = await connect(config);
} catch (err) {
// if connection failed due to an error, wait n seconds and retry
console.log('failed wait 2 seconds for reconnect');
await wait(2000)
// ++retries
}
}
if (!client) {
throw new Error('connection failed due to max number of retries reached.')
}
return client
}
async function main() {
let connection = await open()
}
main().catch(err => console.log(err))
You can further extend this snippet by adding a retry limit. See the comments for a rough idea on how that can be achieved.
To test the above code, you would write:
it("###test", function() {
return open(config);
});
Someone posted an answer about fake timers and then deleted it , The answer was correct so I re-posted again.
You can use sinonjs to create fake timers
Fake timers are synchronous implementations of setTimeout and friends
that Sinon.JS can overwrite the global functions with to allow you to
more easily test code using them
But from your code, it seems you are trying to test async code, in mocha, this can be achieved like this
describe('somthing', () => {
it('the result is 2', async () => {
const x = await add(1, 1)
expect(x).to.equal(4);
});
});
With something closer to your code
async function open() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('done')
}, 1000);
});
};
describe('somthing', () => {
it('###test', async () => {
const x = await open()
chai.expect(x).to.equal("done");
});
});
Just wrap to Promise
const open = async function (config) {
...... set of lines..
return new Promise((resolve, reject) => {
let retryIn = setInterval(async () => {
client = await connect(asConfigParam);
clearInterval(retryIn);
return client;
}, 20000);
return resolve(retryIn);
});
};
it("###test", async () => {
const result = await open(config);
console.log('result', result)
});
UPDATE
I have read over a dozen articles on this topic and not one of them addresses this fundamental question. I am going to start listing a resources section at the end of this post.
ORIGINAL POST
My understanding of an async function is it returns a promise.
MDN docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Inside my program I could write something like:
function testPromise() {
return new Promise((resolve, reject) => {
// DO WORK
reject() // IF WORK FAILS
resolve() // IF WORK IS SUCCESSFUL
})
}
async function mainFunction() {
let variable
try {
variable = await testPromise()
} catch(e) {
throw e
}
return variable
}
I could also write testPromise as an async function and await that in the same context.
async function testAsyncFunction() {
//DO WORK AND THROW ERROR IF THEY OCCUR
}
async function mainFunction() {
let variable
try {
variable = await testAsyncFunction()
} catch(e) {
throw e
}
return variable
}
Which would be considered best practice? If I wish to create asynchronous operation, should the function use return New Promise and awaited in a async function or is awaiting an async function from an async function the same difference?
RESOURCES
JavaScript ES 2017: Learn Async/Await by Example
https://codeburst.io/javascript-es-2017-learn-async-await-by-example-48acc58bad65
Javascript — ES8 Introducing async/await Functions
https://medium.com/#reasoncode/javascript-es8-introducing-async-await-functions-7a471ec7de8a
6 Reasons Why JavaScript’s Async/Await Blows Promises Away (Tutorial)
https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
----------------------CURRENT----------------------
export default function time_neo4jUpdate({ store, action, change, args, }) {
return new Promise(async (resolve, reject) => {
try {
const {
thing: { type },
nonValidatedArgs: { leapYear, root, start, end },
_neo4j,
_cypherReducers,
_neo4jCreateReduce,
_timetreeSteps: { update }
} = store.getState()
let results = []
for (let i = 0; i < _neo4jCreateReduce.length; i++) {
const result = await _neo4j.session(
_neo4jCreateReduce[i],
_cypherReducers.runQuery(update, i, root, start, end))
results = [...results, result]
}
resolve({
store,
action: 'NEO4J_UPDATE',
change: results,
args
})
} catch (e) {
const m = `NEO4J TIMETREE UPDATE: Unable to complete the UPDATE step for the timetree. WHAT: ${e}`
reject(m)
}
})
}
----------------------AS ASYNC FUNCTION----------------------
export default async function time_neo4jUpdate({ store, action, change, args, }) {
try {
const {
thing: { type },
nonValidatedArgs: { leapYear, root, start, end },
_neo4j,
_cypherReducers,
_neo4jCreateReduce,
_timetreeSteps: { update }
} = store.getState()
let results = []
for (let i = 0; i < _neo4jCreateReduce.length; i++) {
const result = await _neo4j.session(
_neo4jCreateReduce[i],
_cypherReducers.runQuery(update, i, root, start, end))
results = [...results, result]
}
return {
store,
action: 'NEO4J_UPDATE',
change: results,
args
}
} catch (e) {
const m = `NEO4J TIMETREE UPDATE: Unable to complete the UPDATE step for the timetree. WHAT: ${e}`
throw m
}
}
Even without the availability of async/await, you should very rarely need to use new Promise(). If you're using it a lot, it's typically a code smell.
The whole point of async/await is that it allows you to avoid a lot of the situations where you would otherwise need to work with promises explicitly.
So if it's supported in the environment you're targeting (Internet Explorer does not support async/await) or you're using a transpiler, go ahead and use it anywhere you can. That's what it's for.
Bear in mind that this is pointless:
catch(e) {
throw e;
}
There's no point in catching an error just to rethrow it. So if you're not actually doing anything with the caught error, don't catch it:
async function testAsyncFunction() {
//DO WORK AND THROW ERROR IF THEY OCCUR
return value
}
Edit: Now that you've provided an example of your code, I can answer with more certainty: If your function is based upon existing promises, then by all means, it is good to use async/await and you usually should not use new Promise():
export default async function time_neo4jUpdate({
store,
action,
change,
args,
}) {
try {
const {
thing: {
type
},
nonValidatedArgs: {
leapYear,
root,
start,
end
},
_neo4j,
_cypherReducers,
_neo4jCreateReduce,
_timetreeSteps: {
update
}
} = store.getState()
const results = await _neo4jCreateReduce.reduce(async function (acc, el) {
const result = await _neo4j.session(
el,
_cypherReducers.runQuery(
update,
i,
root,
start,
end
)
)
return [...(await acc), result]
}, []);
return {
store,
action: 'NEO4J_UPDATE',
change: results,
args
};
} catch (e) {
const m = `NEO4J TIMETREE UPDATE: Unable to complete the UPDATE step for the timetree. WHAT: ${e}`
throw m;
}
}
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
});