Ava: Unhandled rejection error on "t.throws()" test of trivial async function

Created on 27 Apr 2017  ยท  9Comments  ยท  Source: avajs/ava

Description

Unhandled rejection error on test of async function that throws

Test Source

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, '');
});

Error Message & Stack Trace

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)

Config

{
  "ava": {
  "require": [
      "babel-register",
      "babel-polyfill"
    ]
  }
}

Command-Line Arguments

ava -w

Environment

Node.js v7.6.0
darwin 15.6.0
ava 0.19.1
npm 4.1.2

enhancement help wanted assertions

Most helpful comment

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.

๐Ÿ‘

All 9 comments

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.

Was this page helpful?
0 / 5 - 0 ratings