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: provide extra context in case of failed error validation checks #28263

Closed
107 changes: 58 additions & 49 deletions doc/api/assert.md
Expand Up @@ -220,8 +220,9 @@ are also recursively evaluated by the following rules.
* [`Symbol`][] properties are not compared.
* [`WeakMap`][] and [`WeakSet`][] comparison does not rely on their values.

The following example does not throw an `AssertionError` because the primitives
are considered equal by the [Abstract Equality Comparison][] ( `==` ).
The following example does not throw an [`AssertionError`][] because the
primitives are considered equal by the [Abstract Equality Comparison][]
( `==` ).

```js
// WARNING: This does not throw an AssertionError!
Expand Down Expand Up @@ -266,11 +267,11 @@ assert.deepEqual(obj1, obj4);
// AssertionError: { a: { b: 1 } } deepEqual {}
```

If the values are not equal, an `AssertionError` is thrown with a `message`
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. If the `message`
parameter is an instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.
[`AssertionError`][].

## assert.deepStrictEqual(actual, expected[, message])
<!-- YAML
Expand Down Expand Up @@ -419,7 +420,7 @@ assert.deepStrictEqual(weakMap1, weakMap3);
// }
```

If the values are not equal, an `AssertionError` is thrown with a `message`
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. If the `message`
parameter is an instance of an [`Error`][] then it will be thrown instead of the
Expand Down Expand Up @@ -500,9 +501,9 @@ When `assert.doesNotThrow()` is called, it will immediately call the `fn`
function.

If an error is thrown and it is the same type as that specified by the `error`
parameter, then an `AssertionError` is thrown. If the error is of a different
type, or if the `error` parameter is undefined, the error is propagated back
to the caller.
parameter, then an [`AssertionError`][] is thrown. If the error is of a
different type, or if the `error` parameter is undefined, the error is
propagated back to the caller.

If specified, `error` can be a [`Class`][], [`RegExp`][] or a validation
function. See [`assert.throws()`][] for more details.
Expand All @@ -520,7 +521,7 @@ assert.doesNotThrow(
);
```

However, the following will result in an `AssertionError` with the message
However, the following will result in an [`AssertionError`][] with the message
'Got unwanted exception...':

<!-- eslint-disable no-restricted-syntax -->
Expand All @@ -533,8 +534,8 @@ assert.doesNotThrow(
);
```

If an `AssertionError` is thrown and a value is provided for the `message`
parameter, the value of `message` will be appended to the `AssertionError`
If an [`AssertionError`][] is thrown and a value is provided for the `message`
parameter, the value of `message` will be appended to the [`AssertionError`][]
message:

<!-- eslint-disable no-restricted-syntax -->
Expand Down Expand Up @@ -582,7 +583,7 @@ assert.equal({ a: { b: 1 } }, { a: { b: 1 } });
// AssertionError: { a: { b: 1 } } == { a: { b: 1 } }
```

If the values are not equal, an `AssertionError` is thrown with a `message`
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. If the `message`
parameter is an instance of an [`Error`][] then it will be thrown instead of the
Expand All @@ -594,9 +595,9 @@ added: v0.1.21
-->
* `message` {string|Error} **Default:** `'Failed'`

Throws an `AssertionError` with the provided error message or a default error
message. If the `message` parameter is an instance of an [`Error`][] then it
will be thrown instead of the `AssertionError`.
Throws an [`AssertionError`][] with the provided error message or a default
error message. If the `message` parameter is an instance of an [`Error`][] then
it will be thrown instead of the [`AssertionError`][].

```js
const assert = require('assert').strict;
Expand Down Expand Up @@ -683,7 +684,7 @@ changes:
- version: v10.0.0
pr-url: https://github.com/nodejs/node/pull/18247
description: Instead of throwing the original error it is now wrapped into
an `AssertionError` that contains the full stack trace.
an [`AssertionError`][] that contains the full stack trace.
- version: v10.0.0
pr-url: https://github.com/nodejs/node/pull/18247
description: Value may now only be `undefined` or `null`. Before all falsy
Expand Down Expand Up @@ -789,11 +790,11 @@ assert.notDeepEqual(obj1, obj4);
// OK
```

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. If the `message`
parameter is an instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.
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. 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 @@ -836,11 +837,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
`message` parameter is an instance of an [`Error`][] then it will be thrown
instead of the `AssertionError`.
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 @@ -874,10 +875,10 @@ assert.notEqual(1, '1');
// AssertionError: 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. If the `message` parameter is an
instance of an [`Error`][] then it will be thrown instead of the
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. If the `message`
parameter is an instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.

## assert.notStrictEqual(actual, expected[, message])
Expand Down Expand Up @@ -910,11 +911,11 @@ assert.notStrictEqual(1, '1');
// OK
```

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. If the `message`
parameter is an instance of an [`Error`][] then it will be thrown instead of the
`AssertionError`.
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. 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 @@ -931,7 +932,7 @@ changes:
Tests if `value` is truthy. It is equivalent to
`assert.equal(!!value, true, message)`.

If `value` is not truthy, an `AssertionError` is thrown with a `message`
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. If the `message`
parameter is an instance of an [`Error`][] then it will be thrown instead of the
Expand Down Expand Up @@ -1009,8 +1010,8 @@ an object where each property will be tested for, or an instance of error where
each property will be tested for including the non-enumerable `message` and
`name` properties.

If specified, `message` will be the message provided by the `AssertionError` if
the `asyncFn` fails to reject.
If specified, `message` will be the message provided by the [`AssertionError`][]
if the `asyncFn` fails to reject.

```js
(async () => {
Expand Down Expand Up @@ -1076,11 +1077,11 @@ assert.strictEqual('Hello foobar', 'Hello World!');
// ^
```

If the values are not strictly equal, an `AssertionError` is thrown with a
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. If the
`message` parameter is an instance of an [`Error`][] then it will be thrown
instead of the `AssertionError`.
instead of the [`AssertionError`][].

## assert.throws(fn[, error][, message])
<!-- YAML
Expand Down Expand Up @@ -1171,10 +1172,15 @@ assert.throws(
assert.throws(
() => {
const otherErr = new Error('Not found');
otherErr.code = 404;
// Copy all enumerable properties from `err` to `otherErr`.
for (const [key, value] of Object.entries(err)) {
otherErr[key] = value;
}
throw otherErr;
},
err // This tests for `message`, `name` and `code`.
// The error's `message` and `name` properties will also be checked when using
// an error as validation object.
err
);
```

Expand Down Expand Up @@ -1205,6 +1211,9 @@ assert.throws(

Custom error validation:

The function must return `true` to indicate all internal validations passed.
It will otherwise fail with an [`AssertionError`][].

```js
assert.throws(
() => {
Expand All @@ -1214,9 +1223,10 @@ assert.throws(
assert(err instanceof Error);
assert(/value/.test(err));
// Returning anything from validation functions besides `true` is not
// recommended. Doing so results in the caught error being thrown again.
// That is usually not the desired outcome. Throw an error about the
// specific validation that failed instead (as done in this example).
// recommended. By doing that, it's not clear what part of the validation
// failed. Instead, throw an error about the specific validation that failed
// (as done in this example) and add as much helpful debugging information
// to that error as possible.
return true;
},
'unexpected error'
Expand Down Expand Up @@ -1258,16 +1268,15 @@ assert.throws(notThrowing, 'Second');
// It does not throw because the error messages match.
assert.throws(throwingSecond, /Second$/);

// If the error message does not match, the error from within the function is
// not caught.
// If the error message does not match, an AssertionError is thrown.
assert.throws(throwingFirst, /Second$/);
// Error: First
// at throwingFirst (repl:2:9)
// AssertionError [ERR_ASSERTION]
```

Due to the confusing notation, it is recommended not to use a string as the
second argument. This might lead to difficult-to-spot errors.

[`AssertionError`]: #assert_class_assert_assertionerror
[`Class`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
[`ERR_INVALID_RETURN_VALUE`]: errors.html#errors_err_invalid_return_value
[`Error.captureStackTrace`]: errors.html#errors_error_capturestacktrace_targetobject_constructoropt
Expand Down