throws v0.1.7
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.constructoris checked. - An error constructor (a subclass of
ErrororErroritself). If so,error instanceof testis 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
messageproperty 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