DEPRECATED. Use any sane assertion library instead.
Overview
Test if a function throws or not, and optionally if it throws the right thing.
Created to ease testing of errors when using yaba, but can be used anywhere you'd like.
function fn(arg) {
if (typeof arg !== "number") {
throw new TypeError("The argument is invalid. " + arg + " is not a number.")
}
}
// The following assertions pass.
assert(throws(fn))
assert(throws(TypeError, fn))
assert(throws(TypeError("the argument"), fn)) // Note case insensitivity.
assert(throws(TypeError(/invalid|is not/), fn, "args", "to", "be", "passed", "to", fn))
// The following assertions do not pass (wrong error type, non-matching message).
assert(throws(Error("contains|both these strings"), fn))
assert(throws(Error("|matches this exact string|"), fn))
assert(throws(Error("contains this string|" + /and matches this regex/i), fn))
assert(throws(Error("contains this string|/and matches this regex/i"), fn))
throws.splitChar = ";"
assert(throws(Error("using a different split character, so that a pipe (|) can be matched"), fn))
throws.splitChar = "|"
assert(throws(Error(/regexes never have to worry about pipes [|], though/), fn))
throws.splitChar = ","
assert(throws(Error(["using a comma as split character", "allows for using", /an array/]), fn))
// Note that you can put `new` in front of `Error` and `TypeError` if you think that reads better.
What it can look like in CoffeeScript:
assert throws Error("string|#{/regex/}|string2"), -> fn("invalid", "args")
Installation
npm install throws
or component install lydell/throws
CommonJS: var throws = require("throws")
AMD and regular old browser globals: Use ./throws.js
Tests
Node.js: npm test
Browser: Open ./test/browser/index.html
Usage
throws(fn, ...args)
Runs fn(...args)
. Returns true
if it throws. Otherwise false
.
throws(test, fn, ...args)
Runs fn(...args)
. Returns false
if it does not throw. Otherwise the caught error
is compared
to test
.
First off, test
must be one of two things:
- An error instance (
test instanceof Error
). If so,error instanceof test.constructor
is checked. - An error constructor (a subclass of
Error
orError
itself). If so,error instanceof test
is checked.
Secondly, the message
properties (if any) of error
and test
are compared. test.message
is a
pipe ("|") separated string of substrings and regexes. error.message
must contain all substrings,
case insensitively, and match all regexes.
-
To keep things simple, a substring cannot contain a pipe ("|")—there are no escape rules. Instead there is the possibility to change it to something else, via
throws.splitChar
. -
It is, however, possible to escape a regex. They are recognized by a substring beginning with a slash ("/"). Double it to denote an actual substring starting with a slash:
"/a/"
is the regex/a/
, while"//a/"
is the substring"/a/"
. -
If the string starts and ends with a pipe, the whole
message
property of the error must exactly match the string.
If both these tests pass, true
is returned. Otherwise false
.
throws.messageHolder
When throws returns false
, it sets throws.messageHolder.message
to a string explaining why it
did so. This way, you could let other tools use that information.
For example, if you use yaba, you can set throws.messageHolder = yaba
. Then yaba will put the
reason the throws test failed in its assertion errors.
Background
I like testing with assert
, but testing error throwing without a helper function is cumbersome. I
wanted something that fit well with the use of a simple assert function, and let me check the type
of the error, and that certain important key words were present in the error message. After a while
I came up with the idea of comparing to another error instance. Initially I thought about passing an
array of substrings and regexes (new Error(["a", "b", /c/])
), but I then realized that the message
is always converted to a string. After finding out that .toString()
on regexes give a perfect
representation, I understood that it was still possible (and I enjoyed parsing the string!). Best of
all is that passing new Error(/my regex/i)
is totally possible—it looks really nice!
"But I want to check more things on my errors!"
Then throws
probably isn't for you (but don't hesitate to open new issues about it). Either find
something else, or use a good old try/catch:
var error
try {fn(invalid, args)}
catch(err) {error = err}
assert(error && error.lineNumber === 4)
try fn(invalid, args)
catch error
assert error?.lineNumber is 4