Simplify t.throws()
See original GitHub issueThis is my proposal for resolving #661. In short I want to stop deferring to https://nodejs.org/api/assert.html#assert_assert_throws_block_error_message and remove various parameter overloads that are currently supported by the assertion.
Here’s what I’d like to support:
t.throws(fn) // Throws an Error (or subclass thereof)
t.throws(fn, SyntaxError) // Throws a SyntaxError (or subclass thereof)
t.throws(fn, 'string') // Throws an Error (or subclass thereof), whose .message === 'string'
t.throws(fn, /regexp/) // Throws an Error (or subclass thereof), whose /regexp/.test(err.message) === true
If you need to test the errors class and message you should use the t.throws
return value:
const err = t.throws(fn, SyntaxError)
t.true(err.message === 'expecting integer')
Passing anything other than undefined
, functions, strings or regular expressions as the second argument results in an TypeError
from t.throws()
itself.
The first argument is allowed to be a promise or observable, as is any return value from the fn
call. Of course this makes t.throws
asynchronous, so users may need to do:
const err = await t.throws(fn)
Questions:
-
Does
t.throws(fn, Constructor)
require the thrown value to be anError
? For example:t.throws(() => { throw 'foo' }, String) // Is this allowed?
-
Should
t.throws(fn, 'string')
andt.throws(fn, /regexp/)
be supported at all, or would it be preferable to dereference the error and then use another assertion?const err = t.throws(fn) t.true(err.message === 'expecting integer')
-
Is there a need for
t.throws(fn, SyntaxError, 'string')
andt.throws(fn, SyntaxError, /regexp/)
-
Does anybody have experience with / examples of asserting
Error
instances across contexts?
Issue Analytics
- State:
- Created 7 years ago
- Comments:25 (18 by maintainers)
Alternative proposal.
Make it
t.throws(fn, matchers, [message]);
, wherematchers
is an object of different matchers that all has to match. Matchers can be string or regex.This could easily be an addition to supporting string/regex/constructor in the second argument, for backwards compatibility.
For example:
Compared to the current way:
Possible matchers
constructor
,message
. Could also consider supportingname
for when you don’t have easy access to the constructor function. Maybe alsostack
if you want to ensure something is part of it, though I don’t think I’ve ever needed that.Benefits of this is that it makes everything explicit. No more wondering for users what
t.throws(() => foo(), 'Unicorn')
matches against - Is it the name, type, or message. There’s also benefit in being able to specify all the constraints directly instead of in separate calls later, like potential for a better diff.We should also have much better validation logic to prevent mistakes. For example, using
is-error-constructor
on theconstructor
matcher.Yes
It creates verbose code for common cases though. The most common case is wanting to ensure the error is the correct type and has the correct message. I think we should optimize for that.