From 90026c3f3edcb9e930e3f37a0dc41dfb88942a0b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 26 Apr 2018 03:06:10 +0200 Subject: [PATCH] doc: improve assert docs 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: https://github.com/nodejs/node/pull/20313 Reviewed-By: Vse Mozhet Byt Reviewed-By: Rich Trott Reviewed-By: Trivikram Kamat --- doc/api/assert.md | 209 ++++++++++++++++++++++++++++------------------ 1 file changed, 128 insertions(+), 81 deletions(-) diff --git a/doc/api/assert.md b/doc/api/assert.md index ab4aa9c7cd2499..468293b208a90d 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -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. @@ -319,9 +313,14 @@ 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(); @@ -329,33 +328,52 @@ 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([[{}, {}]]); @@ -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` @@ -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; @@ -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]) @@ -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 @@ -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) ``` @@ -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 @@ -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 @@ -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: ```js @@ -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'); @@ -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) ```