Skip to content

Commit

Permalink
doc: improve assert docs
Browse files Browse the repository at this point in the history
This improves the error example output by reflecting the current
state. It also makes sure the examples are up to date in general.
`assert.throws` clarified the `ERR_AMBIGUOUS_ARGUMENT` error.

PR-URL: #20313
Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
  • Loading branch information
BridgeAR authored and MylesBorins committed May 4, 2018
1 parent 7293c58 commit 90026c3
Showing 1 changed file with 128 additions and 81 deletions.
209 changes: 128 additions & 81 deletions doc/api/assert.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,31 +102,25 @@ It can be accessed using:
const assert = require('assert').strict;
```

Example error diff (the `expected`, `actual`, and `Lines skipped` will be on a
single row):
Example error diff:

```js
const assert = require('assert').strict;

assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]);
```

```diff
AssertionError [ERR_ASSERTION]: Input A expected to deepStrictEqual input B:
+ expected
- actual
... Lines skipped

[
[
...
2,
- 3
+ '3'
],
...
5
]
// AssertionError: Input A expected to strictly deep-equal input B:
// + expected - actual ... Lines skipped
//
// [
// [
// ...
// 2,
// - 3
// + '3'
// ],
// ...
// 5
// ]
```

To deactivate the colors, use the `NODE_DISABLE_COLORS` environment variable.
Expand Down Expand Up @@ -319,43 +313,67 @@ are recursively evaluated also by the following rules.
```js
const assert = require('assert').strict;

// This fails because 1 !== '1'.
assert.deepStrictEqual({ a: 1 }, { a: '1' });
// AssertionError: { a: 1 } deepStrictEqual { a: '1' }
// because 1 !== '1' using SameValue comparison
// AssertionError: Input A expected to strictly deep-equal input B:
// + expected - actual
// {
// - a: 1
// + a: '1'
// }

// The following objects don't have own properties
const date = new Date();
const object = {};
const fakeDate = {};
Object.setPrototypeOf(fakeDate, Date.prototype);

// Different [[Prototype]]:
assert.deepStrictEqual(object, fakeDate);
// AssertionError: {} deepStrictEqual Date {}
// Different [[Prototype]]
// AssertionError: Input A expected to strictly deep-equal input B:
// + expected - actual
// - {}
// + Date {}

// Different type tags:
assert.deepStrictEqual(date, fakeDate);
// AssertionError: 2017-03-11T14:25:31.849Z deepStrictEqual Date {}
// Different type tags
// AssertionError: Input A expected to strictly deep-equal input B:
// + expected - actual
// - 2018-04-26T00:49:08.604Z
// + Date {}

assert.deepStrictEqual(NaN, NaN);
// OK, because of the SameValue comparison

// Different unwrapped numbers:
assert.deepStrictEqual(new Number(1), new Number(2));
// Fails because the wrapped number is unwrapped and compared as well.
// AssertionError: Input A expected to strictly deep-equal input B:
// + expected - actual
// - [Number: 1]
// + [Number: 2]

assert.deepStrictEqual(new String('foo'), Object('foo'));
// OK because the object and the string are identical when unwrapped.

assert.deepStrictEqual(-0, -0);
// OK

// Different zeros using the SameValue Comparison:
assert.deepStrictEqual(0, -0);
// AssertionError: 0 deepStrictEqual -0
// AssertionError: Input A expected to strictly deep-equal input B:
// + expected - actual
// - 0
// + -0

const symbol1 = Symbol();
const symbol2 = Symbol();
assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol1]: 1 });
// OK, because it is the same symbol on both objects.
assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol2]: 1 });
// Fails because symbol1 !== symbol2!
// AssertionError [ERR_ASSERTION]: Input objects not identical:
// {
// [Symbol()]: 1
// }

const weakMap1 = new WeakMap();
const weakMap2 = new WeakMap([[{}, {}]]);
Expand All @@ -364,8 +382,16 @@ weakMap3.unequal = true;

assert.deepStrictEqual(weakMap1, weakMap2);
// OK, because it is impossible to compare the entries

// Fails because weakMap3 has a property that weakMap1 does not contain:
assert.deepStrictEqual(weakMap1, weakMap3);
// Fails because weakMap3 has a property that weakMap1 does not contain!
// AssertionError: Input A expected to strictly deep-equal input B:
// + expected - actual
// WeakMap {
// - [items unknown]
// + [items unknown],
// + unequal: true
// }
```

If the values are not equal, an `AssertionError` is thrown with a `message`
Expand Down Expand Up @@ -639,7 +665,9 @@ changes:
* `value` {any}

Throws `value` if `value` is not `undefined` or `null`. This is useful when
testing the `error` argument in callbacks.
testing the `error` argument in callbacks. The stack trace contains all frames
from the error passed to `ifError()` including the potential new frames for
`ifError()` itself. See below for an example.

```js
const assert = require('assert').strict;
Expand All @@ -652,6 +680,19 @@ assert.ifError('error');
// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 'error'
assert.ifError(new Error());
// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: Error

// Create some random error frames.
let err;
(function errorFrame() {
err = new Error('test error');
})();

(function ifErrorFrame() {
assert.ifError(err);
})();
// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error
// at ifErrorFrame
// at errorFrame
```

## assert.notDeepEqual(actual, expected[, message])
Expand Down Expand Up @@ -834,7 +875,7 @@ assert.notStrictEqual(1, 2);
// OK

assert.notStrictEqual(1, 1);
// AssertionError: 1 notStrictEqual 1
// AssertionError [ERR_ASSERTION]: Identical input passed to notStrictEqual: 1

assert.notStrictEqual(1, '1');
// OK
Expand Down Expand Up @@ -880,40 +921,34 @@ assert.ok(1);
// OK

assert.ok();
// throws:
// "AssertionError: No value argument passed to `assert.ok`.
// AssertionError: No value argument passed to `assert.ok()`

assert.ok(false, 'it\'s false');
// throws "AssertionError: it's false"
// AssertionError: it's false

// In the repl:
assert.ok(typeof 123 === 'string');
// throws:
// "AssertionError: false == true
// AssertionError: false == true

// In a file (e.g. test.js):
assert.ok(typeof 123 === 'string');
// throws:
// "AssertionError: The expression evaluated to a falsy value:
// AssertionError: The expression evaluated to a falsy value:
//
// assert.ok(typeof 123 === 'string')

assert.ok(false);
// throws:
// "AssertionError: The expression evaluated to a falsy value:
// AssertionError: The expression evaluated to a falsy value:
//
// assert.ok(false)

assert.ok(0);
// throws:
// "AssertionError: The expression evaluated to a falsy value:
// AssertionError: The expression evaluated to a falsy value:
//
// assert.ok(0)

// Using `assert()` works the same:
assert(0);
// throws:
// "AssertionError: The expression evaluated to a falsy value:
// AssertionError: The expression evaluated to a falsy value:
//
// assert(0)
```
Expand Down Expand Up @@ -995,13 +1030,19 @@ determined by the [SameValue Comparison][].
const assert = require('assert').strict;

assert.strictEqual(1, 2);
// AssertionError: 1 strictEqual 2
// AssertionError [ERR_ASSERTION]: Input A expected to strictly equal input B:
// + expected - actual
// - 1
// + 2

assert.strictEqual(1, 1);
// OK

assert.strictEqual(1, '1');
// AssertionError: 1 strictEqual '1'
// AssertionError [ERR_ASSERTION]: Input A expected to strictly equal input B:
// + expected - actual
// - 1
// + '1'
```

If the values are not strictly equal, an `AssertionError` is thrown with a
Expand Down Expand Up @@ -1035,6 +1076,34 @@ each property will be tested for including the non-enumerable `message` and
If specified, `message` will be the message provided by the `AssertionError` if
the block fails to throw.

Custom error object / error instance:

```js
const err = new TypeError('Wrong value');
err.code = 404;

assert.throws(
() => {
throw err;
},
{
name: 'TypeError',
message: 'Wrong value'
// Note that only properties on the error object will be tested!
}
);

// Fails due to the different `message` and `name` properties:
assert.throws(
() => {
const otherErr = new Error('Not found');
otherErr.code = 404;
throw otherErr;
},
err // This tests for `message`, `name` and `code`.
);
```

Validate instanceof using constructor:

```js
Expand Down Expand Up @@ -1076,39 +1145,12 @@ assert.throws(
);
```

Custom error object / error instance:

```js
const err = new TypeError('Wrong value');
err.code = 404;

assert.throws(
() => {
throw err;
},
{
name: 'TypeError',
message: 'Wrong value'
// Note that only properties on the error object will be tested!
}
);

// Fails due to the different `message` and `name` properties:
assert.throws(
() => {
const otherErr = new Error('Not found');
otherErr.code = 404;
throw otherErr;
},
err // This tests for `message`, `name` and `code`.
);
```

Note that `error` cannot be a string. If a string is provided as the second
argument, then `error` is assumed to be omitted and the string will be used for
`message` instead. This can lead to easy-to-miss mistakes. Please read the
example below carefully if using a string as the second argument gets
considered:
`message` instead. This can lead to easy-to-miss mistakes. Using the same
message as the thrown error message is going to result in an
`ERR_AMBIGUOUS_ARGUMENT` error. Please read the example below carefully if using
a string as the second argument gets considered:

<!-- eslint-disable no-restricted-syntax -->
```js
Expand All @@ -1121,10 +1163,15 @@ function throwingSecond() {
function notThrowing() {}

// The second argument is a string and the input function threw an Error.
// In that case both cases do not throw as neither is going to try to
// match for the error message thrown by the input function!
// The first case will not throw as it does not match for the error message
// thrown by the input function!
assert.throws(throwingFirst, 'Second');
// In the next example the message has no benefit over the message from the
// error and since it is not clear if the user intended to actually match
// against the error message, Node.js thrown an `ERR_AMBIGUOUS_ARGUMENT` error.
assert.throws(throwingSecond, 'Second');
// Throws an error:
// TypeError [ERR_AMBIGUOUS_ARGUMENT]

// The string is only used (as message) in case the function does not throw:
assert.throws(notThrowing, 'Second');
Expand All @@ -1134,7 +1181,7 @@ assert.throws(notThrowing, 'Second');
assert.throws(throwingSecond, /Second$/);
// Does not throw because the error messages match.
assert.throws(throwingFirst, /Second$/);
// Throws a error:
// Throws an error:
// Error: First
// at throwingFirst (repl:2:9)
```
Expand Down

0 comments on commit 90026c3

Please sign in to comment.