throwsdeprecated

Helps testing error throwing of functions.

Usage no npm install needed!

<script type="module">
  import throws from 'https://cdn.skypack.dev/throws';
</script>

README

Build Status

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 or Error 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

License

LGPLv3.