Skip to content

Commit

Permalink
assert: support custom errors
Browse files Browse the repository at this point in the history
This commit adds special handling of Error instances when passed
as the message argument to assert functions. With this commit,
if an Error is passed as the message, then that Error is thrown
instead of an AssertionError.

PR-URL: #15304
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
  • Loading branch information
geek authored and cjihrig committed Sep 12, 2017
1 parent a10856a commit e13d1df
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 18 deletions.
59 changes: 41 additions & 18 deletions doc/api/assert.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ assert.deepEqual(obj1, obj4);

If the values are not equal, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is undefined, a default error message is assigned.
parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
`AssertionError`.

## assert.deepStrictEqual(actual, expected[, message])
<!-- YAML
Expand Down Expand Up @@ -176,7 +178,9 @@ assert.deepStrictEqual(NaN, NaN);

If the values are not equal, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is undefined, a default error message is assigned.
parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
`AssertionError`.

## assert.doesNotThrow(block[, error][, message])
<!-- YAML
Expand Down Expand Up @@ -270,7 +274,9 @@ assert.equal({ a: { b: 1 } }, { a: { b: 1 } });

If the values are not equal, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is undefined, a default error message is assigned.
parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
`AssertionError`.

## assert.fail([message])
## assert.fail(actual, expected[, message[, operator[, stackStartFunction]]])
Expand All @@ -284,13 +290,15 @@ added: v0.1.21
* `stackStartFunction` {function} (default: `assert.fail`)

Throws an `AssertionError`. If `message` is falsy, the error message is set as
the values of `actual` and `expected` separated by the provided `operator`.
If just the two `actual` and `expected` arguments are provided, `operator` will
default to `'!='`. If `message` is provided only it will be used as the error
message, the other arguments will be stored as properties on the thrown object.
If `stackStartFunction` is provided, all stack frames above that function will
be removed from stacktrace (see [`Error.captureStackTrace`]). If no arguments
are given, the default message `Failed` will be used.
the values of `actual` and `expected` separated by the provided `operator`. If
the `message` parameter is an instance of an `Error` then it will be thrown
instead of the `AssertionError`. If just the two `actual` and `expected`
arguments are provided, `operator` will default to `'!='`. If `message` is
provided only it will be used as the error message, the other arguments will be
stored as properties on the thrown object. If `stackStartFunction` is provided,
all stack frames above that function will be removed from stacktrace (see
[`Error.captureStackTrace`]). If no arguments are given, the default message
`Failed` will be used.

```js
const assert = require('assert');
Expand All @@ -303,6 +311,9 @@ assert.fail(1, 2, 'fail');

assert.fail(1, 2, 'whoops', '>');
// AssertionError [ERR_ASSERTION]: whoops

assert.fail(1, 2, new TypeError('need array'));
// TypeError: need array
```

*Note*: Is the last two cases `actual`, `expected`, and `operator` have no
Expand Down Expand Up @@ -414,7 +425,9 @@ assert.notDeepEqual(obj1, obj4);

If the values are deeply equal, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is undefined, a default error message is assigned.
parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
`AssertionError`.

## assert.notDeepStrictEqual(actual, expected[, message])
<!-- YAML
Expand Down Expand Up @@ -455,9 +468,11 @@ assert.notDeepStrictEqual({ a: 1 }, { a: '1' });
// OK
```

If the values are deeply and strictly equal, an `AssertionError` is thrown
with a `message` property set equal to the value of the `message` parameter. If
the `message` parameter is undefined, a default error message is assigned.
If the values are deeply and strictly equal, an `AssertionError` is thrown with
a `message` property set equal to the value of the `message` parameter. If the
`message` parameter is undefined, a default error message is assigned. If the
`message` parameter is an instance of an `Error` then it will be thrown instead
of the `AssertionError`.

## assert.notEqual(actual, expected[, message])
<!-- YAML
Expand Down Expand Up @@ -485,7 +500,9 @@ assert.notEqual(1, '1');

If the values are equal, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is undefined, a default error message is assigned.
parameter is undefined, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
`AssertionError`.

## assert.notStrictEqual(actual, expected[, message])
<!-- YAML
Expand Down Expand Up @@ -513,7 +530,9 @@ assert.notStrictEqual(1, '1');

If the values are strictly equal, an `AssertionError` is thrown with a
`message` property set equal to the value of the `message` parameter. If the
`message` parameter is undefined, a default error message is assigned.
`message` parameter is undefined, a default error message is assigned. If the
`message` parameter is an instance of an `Error` then it will be thrown instead
of the `AssertionError`.

## assert.ok(value[, message])
<!-- YAML
Expand All @@ -527,7 +546,9 @@ Tests if `value` is truthy. It is equivalent to

If `value` is not truthy, an `AssertionError` is thrown with a `message`
property set equal to the value of the `message` parameter. If the `message`
parameter is `undefined`, a default error message is assigned.
parameter is `undefined`, a default error message is assigned. If the `message`
parameter is an instance of an `Error` then it will be thrown instead of the
`AssertionError`.

```js
const assert = require('assert');
Expand Down Expand Up @@ -570,7 +591,9 @@ assert.strictEqual(1, '1');

If the values are not strictly equal, an `AssertionError` is thrown with a
`message` property set equal to the value of the `message` parameter. If the
`message` parameter is undefined, a default error message is assigned.
`message` parameter is undefined, a default error message is assigned. If the
`message` parameter is an instance of an `Error` then it will be thrown instead
of the `AssertionError`.

## assert.throws(block[, error][, message])
<!-- YAML
Expand Down
2 changes: 2 additions & 0 deletions lib/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const assert = module.exports = ok;
// display purposes.

function innerFail(actual, expected, message, operator, stackStartFunction) {
if (message instanceof Error) throw message;

throw new errors.AssertionError({
message,
actual,
Expand Down
22 changes: 22 additions & 0 deletions test/parallel/test-assert-fail.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ common.expectsError(() => {
expected: undefined
});

// One arg = Error
common.expectsError(() => {
assert.fail(new TypeError('custom message'));
}, {
type: TypeError,
message: 'custom message',
operator: undefined,
actual: undefined,
expected: undefined
});

// Two args only, operator defaults to '!='
common.expectsError(() => {
assert.fail('first', 'second');
Expand All @@ -52,6 +63,17 @@ common.expectsError(() => {
expected: 'ignored'
});

// Three args with custom Error
common.expectsError(() => {
assert.fail(typeof 1, 'object', new TypeError('another custom message'));
}, {
type: TypeError,
message: 'another custom message',
operator: undefined,
actual: 'number',
expected: 'object'
});

// No third arg (but a fourth arg)
common.expectsError(() => {
assert.fail('first', 'second', undefined, 'operator');
Expand Down
28 changes: 28 additions & 0 deletions test/parallel/test-assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,34 @@ try {
'Message incorrectly marked as generated');
}

{
let threw = false;
const rangeError = new RangeError('my range');

// verify custom errors
try {
assert.strictEqual(1, 2, rangeError);
} catch (e) {
assert.strictEqual(e, rangeError);
threw = true;
assert.ok(e instanceof RangeError, 'Incorrect error type thrown');
}
assert.ok(threw);
threw = false;

// verify AssertionError is the result from doesNotThrow with custom Error
try {
assert.doesNotThrow(() => {
throw new TypeError('wrong type');
}, TypeError, rangeError);
} catch (e) {
threw = true;
assert.ok(e.message.includes(rangeError.message));
assert.ok(e instanceof assert.AssertionError);
}
assert.ok(threw);
}

{
// Verify that throws() and doesNotThrow() throw on non-function block
function typeName(value) {
Expand Down

0 comments on commit e13d1df

Please sign in to comment.