Unhandled rejection error on test of async function that throws
import test from 'ava';
const f = async () => {
throw new Error();
};
test(async t => {
const error = await t.throws(async () => {
return await f();
});
t.is(error.message, '');
});
1 failed [08:28:15]
1 rejection
test โบ [anonymous]
/Users/scotty/workspace/fetch-nice/test/test.js:9
8:
9: const error = await t.throws(async () => {
10: return await f();
Test.<anonymous> (test/test.js:9:27)
step (test/test.js:9:191)
Test.<anonymous> (test/test.js:9:99)
Test.__dirname [as fn] (test/test.js:7:1)
Unhandled Rejection
Error
test/test.js:4:11
Generator.next (<anonymous>)
step (test/test.js:9:191)
test/test.js:9:437
test/test.js:9:99
f (test/test.js:3:7)
test/test.js:10:22
Generator.next (<anonymous>)
step (test/test.js:9:191)
test/test.js:9:437
test/test.js:9:99
_tryBlock (node_modules/core-assert/index.js:311:5)
_throws (node_modules/core-assert/index.js:330:12)
Function.assert.throws (node_modules/core-assert/index.js:360:3)
Test.<anonymous> (test/test.js:9:27)
Generator.next (<anonymous>)
step (test/test.js:9:191)
test/test.js:9:437
Test.<anonymous> (test/test.js:9:99)
Test.__dirname [as fn] (test/test.js:7:1)
{
"ava": {
"require": [
"babel-register",
"babel-polyfill"
]
}
}
ava -w
Node.js v7.6.0
darwin 15.6.0
ava 0.19.1
npm 4.1.2
Simpler test case:
import test from 'ava';
test(async t => {
await t.throws(async () => {
throw new Error();
});
});
This is correct behavior: the function doesn't throw, so the assertion fails. AVA ignores the return value, so the promise rejection ends up being unhandled and is reported separately.
That said I think we should extend t.throws() and t.notThrows() so that if the function returns a promise without throwing, the assertion is applied to that promise instead.
Is this not effectively the same as the following example in the t.throws() docs here?
const promise = Promise.reject(new TypeError('๐ฆ'));
test('rejects', async t => {
const error = await t.throws(promise);
t.is(error.message, '๐ฆ');
});
@sdd No it's the same as:
import test from 'ava';
const promise = () => Promise.reject(new TypeError('๐ฆ'));
test('rejects', async t => {
const error = await t.throws(promise);
t.is(error.message, '๐ฆ');
});
Note the arrow function.
That said I think we should extend t.throws() and t.notThrows() so that if the function returns a promise without throwing, the assertion is applied to that promise instead.
๐
A valid use-case for this design pattern in case you are on the fence:
const someTruthy = true;
const someAsyncThing = await () => {
if (someTruthy) {
throw new TypeError('๐ฆ');
}
await asyncStuff();
await moreAsyncStuff();
}
// currently fails with unhandled rejection
test('rejects', async t => {
await t.throws(someAsyncThing, '๐ฆ');
});
IMO the above design pattern is much nicer than:
// passes
test('rejects', async t => {
await someAsyncThing()
.catch(err => {
t.is(err.message, '๐ฆ');
});
});
Also, it took me a while to discover this is actually functioning as designed, since it seems very similar to the use-case documented here: https://github.com/avajs/ava/#throwsfunctionpromise-error-message
A valid use-case for this design pattern in case you are on the fence:
We're not on the fence, see the issue labels ๐ Help most wanted!
Agree that this would be a lovely feature. As a note to my future self (since I keep making this mistake), a work-around is to use an IIFE to turn the promise-returning function into a promise:
test(async t => {
await t.throws((async () => {
throw new Error();
})());
});
This was fixed in #1650.
Most helpful comment
๐