ava: t.throws shows `undefined undefined undefined` when a string is thrown

Description

When using t.throws() with a promise that rejects with a string instead of an Error object, the assertion fails.

import test from 'ava';

async function fn(): Promise<any> {
    return Promise.reject('foo');
}

test(async t => {
    t.throws(fn(), 'foo');
});
screen shot 2016-03-21 at 11 53 43

When using Promise.reject(new Error('foo'));, it works as expected.

It’s due to these lines. I have no idea why it overrides the err reference with a function. I looked into core-assert and it doesn’t seem like it works with passing in a function as the expected parameter. But might be missing something here.

Environment

Node.js v4.2.1 darwin 15.3.0

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 1
  • Comments: 32 (32 by maintainers)

Most upvoted comments

I guess I can live with t.throwsAny

I can’t.

I think that whether or not throwing a string is allowed should be up to the linter and not the test runner. Given that the language allows you to throw or reject with whatever you want, I would think that t.throws should allow you to do so. I would expect all of the following to be valid:

t.throws(Promise.reject('foo'), String);
t.throws(Promise.reject('foo'), 'some string');
t.throws(Promise.reject('foo'), /somePattern/);

That said, I would never use it, because I use a good linter and only throw Errors.

From the first post of @novemberborn in this thread, I assumed that AVA stopped asserting literals being thrown in the code

That is expected. With 0.13.0 t.throws() only tests against errors, before it would try and cast other types to errors, which was wrong.

But when reading further down, it looks like somehow that behaviour broke or was removed without thinking about the consequences.


I have a double feeling about the solution of throwing an error when a literal is thrown and force the user to throw/reject with an error. Yes in a perfect world that would be very nice. But this might not be the task of a test runner to force a best practice. It might be up to a linter to force the developer to not throw literals.


If we would accept errors and literals, please don’t add an extra method like throwsAny. I think it can be easily avoided.

t.throws(fn(), Error)

This is quite clear, we expect an Error object.

t.throws(fn(), 'Foo Bar') or t.throws(fn(), /Foo Bar/)

2 options here

1. fn() throws an Error

If fn() throws an Error (see is-error or something alike), assert that err.message is Foo Bar.

2. fn() throws a string literal

If fn() throws a string literal, assert that err === Foo Bar.

What’s wrong with something like this? Maybe I’m missing something but this would work for everyone, no?