From c3ed6da1f61fc535a9771b3090315a8ec2f1cf57 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 26 Apr 2018 03:06:10 +0200 Subject: [PATCH 1/4] docs: 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. --- doc/api/assert.md | 121 +++++++++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 34 deletions(-) diff --git a/doc/api/assert.md b/doc/api/assert.md index ab4aa9c7cd2499..00282369ca4fa6 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -112,7 +112,7 @@ assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); ``` ```diff -AssertionError [ERR_ASSERTION]: Input A expected to deepStrictEqual input B: +AssertionError [ERR_ASSERTION]: Input A expected to strictly deep-equal input B: + expected - actual ... Lines skipped @@ -319,9 +319,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 +334,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 +388,16 @@ weakMap3.unequal = true; assert.deepStrictEqual(weakMap1, weakMap2); // OK, because it is impossible to compare the entries + +// Fail 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 +671,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 +686,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 +881,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 +927,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 +1036,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 @@ -1106,9 +1153,10 @@ assert.throws( 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 +1169,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 +1187,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) ``` From 1391428b6e27e1becfe9588e3ce9c8295c162e6b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 26 Apr 2018 04:46:13 +0200 Subject: [PATCH 2/4] fixup: clearer example --- doc/api/assert.md | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/doc/api/assert.md b/doc/api/assert.md index 00282369ca4fa6..c98bc5315ba2de 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 strictly deep-equal 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. From 2da7521b4e808b829496c5023a45b2b575660741 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 26 Apr 2018 13:14:02 +0200 Subject: [PATCH 3/4] fixup: address comments --- doc/api/assert.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/api/assert.md b/doc/api/assert.md index c98bc5315ba2de..d5e6a43d751f95 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -383,7 +383,7 @@ weakMap3.unequal = true; assert.deepStrictEqual(weakMap1, weakMap2); // OK, because it is impossible to compare the entries -// Fail because weakMap3 has a property that weakMap1 does not contain: +// Fails because weakMap3 has a property that weakMap1 does not contain: assert.deepStrictEqual(weakMap1, weakMap3); // AssertionError: Input A expected to strictly deep-equal input B: // + expected - actual @@ -666,8 +666,8 @@ changes: Throws `value` if `value` is not `undefined` or `null`. This is useful when 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. +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; @@ -921,7 +921,7 @@ assert.ok(1); // OK assert.ok(); -// AssertionError: No value argument passed to `assert.ok`. +// AssertionError: No value argument passed to `assert.ok()`. assert.ok(false, 'it\'s false'); // AssertionError: it's false" From 8fd1fe38c6b22fdf45241fe6d226012dc01e3d6a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 27 Apr 2018 14:41:53 +0200 Subject: [PATCH 4/4] fixup: address comments and move example --- doc/api/assert.md | 60 +++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/doc/api/assert.md b/doc/api/assert.md index d5e6a43d751f95..468293b208a90d 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -921,10 +921,10 @@ assert.ok(1); // OK assert.ok(); -// AssertionError: No value argument passed to `assert.ok()`. +// AssertionError: No value argument passed to `assert.ok()` assert.ok(false, 'it\'s false'); -// AssertionError: it's false" +// AssertionError: it's false // In the repl: assert.ok(typeof 123 === 'string'); @@ -1076,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 @@ -1117,34 +1145,6 @@ 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. Using the same