Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

assert: support custom errors #15304

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -174,7 +176,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 @@ -268,7 +272,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 @@ -282,13 +288,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 @@ -301,6 +309,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 @@ -412,7 +423,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 @@ -453,9 +466,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 @@ -483,7 +498,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 @@ -511,7 +528,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 @@ -525,7 +544,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 @@ -568,7 +589,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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would actually be nice to use common.expectsError here. The test would be smaller in that case. You can check some other use cases as a reference.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the suggestion. I tried expectsError, but for this simple case it didn't make the code any cleaner/shorter/simpler. I'm keeping it as is for now.


// 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