diff --git a/doc/api/assert.md b/doc/api/assert.md index 32f7499e953a97..87672be02ecc6f 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -40,17 +40,45 @@ assertion mode, error messages for objects display the objects, often truncated. To use strict assertion mode: -```js +```mjs +import { strict as assert } from 'assert'; +``` + +```cjs const assert = require('assert').strict; ``` -```js + +```mjs +import assert from 'assert/strict'; +``` + +```cjs const assert = require('assert/strict'); ``` Example error diff: -```js -const assert = require('assert').strict; +```mjs +import { strict as assert } from 'assert'; + +assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); +// AssertionError: Expected inputs to be strictly deep-equal: +// + actual - expected ... Lines skipped +// +// [ +// [ +// ... +// 2, +// + 3 +// - '3' +// ], +// ... +// 5 +// ] +``` + +```cjs +const assert = require('assert/strict'); assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); // AssertionError: Expected inputs to be strictly deep-equal: @@ -84,7 +112,11 @@ Legacy assertion mode uses the [Abstract Equality Comparison][] in: To use legacy assertion mode: -```js +```cjs +import assert from 'assert'; +``` + +```cjs const assert = require('assert'); ``` @@ -93,7 +125,7 @@ Whenever possible, use the [strict assertion mode][] instead. Otherwise, the especially true for [`assert.deepEqual()`][], where the comparison rules are lax: -```js +```cjs // WARNING: This does not throw an AssertionError! assert.deepEqual(/a/gi, new Date()); ``` @@ -133,7 +165,32 @@ and: assertion error. * `operator` {string} Set to the passed in operator value. -```js +```mjs +import assert from 'assert'; + +// Generate an AssertionError to compare the error message later: +const { message } = new assert.AssertionError({ + actual: 1, + expected: 2, + operator: 'strictEqual' +}); + +// Verify error output: +try { + assert.strictEqual(1, 2); +} catch (err) { + assert(err instanceof assert.AssertionError); + assert.strictEqual(err.message, message); + assert.strictEqual(err.name, 'AssertionError'); + assert.strictEqual(err.actual, 1); + assert.strictEqual(err.expected, 2); + assert.strictEqual(err.code, 'ERR_ASSERTION'); + assert.strictEqual(err.operator, 'strictEqual'); + assert.strictEqual(err.generatedMessage, true); +} +``` + +```cjs const assert = require('assert'); // Generate an AssertionError to compare the error message later: @@ -181,7 +238,26 @@ were called a specific number of times. The `tracker.verify()` must be called for the verification to take place. The usual pattern would be to call it in a [`process.on('exit')`][] handler. -```js +```mjs +import assert from 'assert'; + +const tracker = new assert.CallTracker(); + +function func() {} + +// callsfunc() must be called exactly 1 time before tracker.verify(). +const callsfunc = tracker.calls(func, 1); + +callsfunc(); + +// Calls tracker.verify() and verifies if all tracker.calls() functions have +// been called exact times. +process.on('exit', () => { + tracker.verify(); +}); +``` + +```cjs const assert = require('assert'); const tracker = new assert.CallTracker(); @@ -216,7 +292,20 @@ function has not been called exactly `exact` times when [`tracker.verify()`][] is called, then [`tracker.verify()`][] will throw an error. -```js +```mjs +import assert from 'assert'; + +// Creates call tracker. +const tracker = new assert.CallTracker(); + +function func() {} + +// Returns a function that wraps func() that must be called exact times +// before tracker.verify(). +const callsfunc = tracker.calls(func); +``` + +```cjs const assert = require('assert'); // Creates call tracker. @@ -249,7 +338,35 @@ added: The arrays contains information about the expected and actual number of calls of the functions that have not been called the expected number of times. -```js +```mjs +import assert from 'assert'; + +// Creates call tracker. +const tracker = new assert.CallTracker(); + +function func() {} + +function foo() {} + +// Returns a function that wraps func() that must be called exact times +// before tracker.verify(). +const callsfunc = tracker.calls(func, 2); + +// Returns an array containing information on callsfunc() +tracker.report(); +// [ +// { +// message: 'Expected the func function to be executed 2 time(s) but was +// executed 0 time(s).', +// actual: 0, +// expected: 2, +// operator: 'func', +// stack: stack trace +// } +// ] +``` + +```cjs const assert = require('assert'); // Creates call tracker. @@ -288,7 +405,25 @@ Iterates through the list of functions passed to [`tracker.calls()`][] and will throw an error for functions that have not been called the expected number of times. -```js +```mjs +import assert from 'assert'; + +// Creates call tracker. +const tracker = new assert.CallTracker(); + +function func() {} + +// Returns a function that wraps func() that must be called exact times +// before tracker.verify(). +const callsfunc = tracker.calls(func, 2); + +callsfunc(); + +// Will throw an error since callsfunc() was only called once. +tracker.verify(); +``` + +```cjs const assert = require('assert'); // Creates call tracker. @@ -393,15 +528,59 @@ The following example does not throw an [`AssertionError`][] because the primitives are considered equal by the [Abstract Equality Comparison][] ( `==` ). -```js +```mjs +import assert from 'assert'; +// WARNING: This does not throw an AssertionError! + +assert.deepEqual('+00000000', false); +``` + +```cjs +const assert = require('assert'); // WARNING: This does not throw an AssertionError! + assert.deepEqual('+00000000', false); ``` "Deep" equality means that the enumerable "own" properties of child objects are evaluated also: -```js +```mjs +import assert from 'assert'; + +const obj1 = { + a: { + b: 1 + } +}; +const obj2 = { + a: { + b: 2 + } +}; +const obj3 = { + a: { + b: 1 + } +}; +const obj4 = Object.create(obj1); + +assert.deepEqual(obj1, obj1); +// OK + +// Values of b are different: +assert.deepEqual(obj1, obj2); +// AssertionError: { a: { b: 1 } } deepEqual { a: { b: 2 } } + +assert.deepEqual(obj1, obj3); +// OK + +// Prototypes are ignored: +assert.deepEqual(obj1, obj4); +// AssertionError: { a: { b: 1 } } deepEqual {} +``` + +```cjs const assert = require('assert'); const obj1 = { @@ -502,8 +681,100 @@ are recursively evaluated also by the following rules. * [`WeakMap`][] and [`WeakSet`][] comparison does not rely on their values. See below for further details. -```js -const assert = require('assert').strict; +```mjs +import assert from 'assert/strict'; + +// This fails because 1 !== '1'. +deepStrictEqual({ a: 1 }, { a: '1' }); +// AssertionError: Expected inputs to be strictly deep-equal: +// + actual - expected +// +// { +// + 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: Expected inputs to be strictly deep-equal: +// + actual - expected +// +// + {} +// - Date {} + +// Different type tags: +assert.deepStrictEqual(date, fakeDate); +// AssertionError: Expected inputs to be strictly deep-equal: +// + actual - expected +// +// + 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)); +// AssertionError: Expected inputs to be strictly deep-equal: +// + actual - expected +// +// + [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: Expected inputs to be strictly deep-equal: +// + actual - expected +// +// + 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 }); +// AssertionError [ERR_ASSERTION]: Inputs identical but not reference equal: +// +// { +// [Symbol()]: 1 +// } + +const weakMap1 = new WeakMap(); +const weakMap2 = new WeakMap([[{}, {}]]); +const weakMap3 = new WeakMap(); +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); +// AssertionError: Expected inputs to be strictly deep-equal: +// + actual - expected +// +// WeakMap { +// + [items unknown] +// - [items unknown], +// - unequal: true +// } +``` + +```cjs +const assert = require('assert/strict'); // This fails because 1 !== '1'. assert.deepStrictEqual({ a: 1 }, { a: '1' }); @@ -618,8 +889,8 @@ Expects the `string` input not to match the regular expression. This feature is currently experimental and the name might change or it might be completely removed again. -```js -const assert = require('assert').strict; +```mjs +import assert from 'assert/strict'; assert.doesNotMatch('I will fail', /fail/); // AssertionError [ERR_ASSERTION]: The input was expected to not match the ... @@ -631,11 +902,24 @@ assert.doesNotMatch('I will pass', /different/); // OK ``` -If the values do match, or if the `string` argument is of another type than -`string`, 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 +```cjs +const assert = require('assert/strict'); + +assert.doesNotMatch('I will fail', /fail/); +// AssertionError [ERR_ASSERTION]: The input was expected to not match the ... + +assert.doesNotMatch(123, /pass/); +// AssertionError [ERR_ASSERTION]: The "string" argument must be of type string. + +assert.doesNotMatch('I will pass', /different/); +// OK +``` + +If the values do match, or if the `string` argument is of another type than +`string`, 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.doesNotReject(asyncFn[, error][, message])` @@ -669,7 +953,20 @@ Besides the async nature to await the completion behaves identically to [`assert.doesNotThrow()`][]. -```js +```mjs +import assert from 'assert/strict'; + +await assert.doesNotReject( + async () => { + throw new TypeError('Wrong value'); + }, + SyntaxError +); +``` + +```cjs +const assert = require('assert/strict'); + (async () => { await assert.doesNotReject( async () => { @@ -681,7 +978,19 @@ Besides the async nature to await the completion behaves identically to ``` -```js +```mjs +import assert from 'assert/strict'; + +assert.doesNotReject(Promise.reject(new TypeError('Wrong value'))) + .then(() => { + // ... + }); +``` + + +```cjs +const assert = require('assert/strict'); + assert.doesNotReject(Promise.reject(new TypeError('Wrong value'))) .then(() => { // ... @@ -728,7 +1037,21 @@ The following, for instance, will throw the [`TypeError`][] because there is no matching error type in the assertion: -```js +```mjs +import assert from 'assert/strict'; + +assert.doesNotThrow( + () => { + throw new TypeError('Wrong value'); + }, + SyntaxError +); +``` + + +```cjs +const assert = require('assert/strict'); + assert.doesNotThrow( () => { throw new TypeError('Wrong value'); @@ -741,7 +1064,21 @@ However, the following will result in an [`AssertionError`][] with the message 'Got unwanted exception...': -```js +```mjs +import assert from 'assert/strict'; + +assert.doesNotThrow( + () => { + throw new TypeError('Wrong value'); + }, + TypeError +); +``` + + +```cjs +const assert = require('assert/strict'); + assert.doesNotThrow( () => { throw new TypeError('Wrong value'); @@ -755,7 +1092,23 @@ parameter, the value of `message` will be appended to the [`AssertionError`][] message: -```js +```mjs +import assert from 'assert/strict'; + +assert.doesNotThrow( + () => { + throw new TypeError('Wrong value'); + }, + /Wrong value/, + 'Whoops' +); +// Throws: AssertionError: Got unwanted exception: Whoops +``` + + +```cjs +const assert = require('assert/strict'); + assert.doesNotThrow( () => { throw new TypeError('Wrong value'); @@ -792,7 +1145,23 @@ Tests shallow, coercive equality between the `actual` and `expected` parameters using the [Abstract Equality Comparison][] ( `==` ). `NaN` is special handled and treated as being identical in case both sides are `NaN`. -```js +```mjs +import assert from 'assert'; + +assert.equal(1, 1); +// OK, 1 == 1 +assert.equal(1, '1'); +// OK, 1 == '1' +assert.equal(NaN, NaN); +// OK + +assert.equal(1, 2); +// AssertionError: 1 == 2 +assert.equal({ a: { b: 1 } }, { a: { b: 1 } }); +// AssertionError: { a: { b: 1 } } == { a: { b: 1 } } +``` + +```cjs const assert = require('assert'); assert.equal(1, 1); @@ -825,8 +1194,21 @@ 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; +```mjs +import assert from 'assert/strict'; + +assert.fail(); +// AssertionError [ERR_ASSERTION]: Failed + +assert.fail('boom'); +// AssertionError [ERR_ASSERTION]: boom + +assert.fail(new TypeError('need array')); +// TypeError: need array +``` + +```cjs +const assert = require('assert/strict'); assert.fail(); // AssertionError [ERR_ASSERTION]: Failed @@ -869,8 +1251,27 @@ the other arguments will be stored as properties on the thrown object. If removed from stacktrace (see [`Error.captureStackTrace`][]). If no arguments are given, the default message `Failed` will be used. -```js -const assert = require('assert').strict; +```mjs +import assert from 'assert/strict'; + +assert.fail('a', 'b'); +// AssertionError [ERR_ASSERTION]: 'a' != 'b' + +assert.fail(1, 2, undefined, '>'); +// AssertionError [ERR_ASSERTION]: 1 > 2 + +assert.fail(1, 2, 'fail'); +// AssertionError [ERR_ASSERTION]: fail + +assert.fail(1, 2, 'whoops', '>'); +// AssertionError [ERR_ASSERTION]: whoops + +assert.fail(1, 2, new TypeError('need array')); +// TypeError: need array +``` + +```cjs +const assert = require('assert/strict'); assert.fail('a', 'b'); // AssertionError [ERR_ASSERTION]: 'a' != 'b' @@ -893,7 +1294,22 @@ influence on the error message. Example use of `stackStartFn` for truncating the exception's stacktrace: -```js +```mjs +import assert from 'assert/strict'; + +function suppressFrame() { + assert.fail('a', 'b', undefined, '!==', suppressFrame); +} +suppressFrame(); +// AssertionError [ERR_ASSERTION]: 'a' !== 'b' +// at repl:1:1 +// at ContextifyScript.Script.runInThisContext (vm.js:44:33) +// ... +``` + +```cjs +const assert = require('assert/strict'); + function suppressFrame() { assert.fail('a', 'b', undefined, '!==', suppressFrame); } @@ -925,8 +1341,34 @@ 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. -```js -const assert = require('assert').strict; +```mjs +import assert from 'assert/strict'; + +assert.ifError(null); +// OK +assert.ifError(0); +// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 0 +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 +``` + +```cjs +const assert = require('assert/strict'); assert.ifError(null); // OK @@ -969,8 +1411,21 @@ Expects the `string` input to match the regular expression. This feature is currently experimental and the name might change or it might be completely removed again. -```js -const assert = require('assert').strict; +```mjs +import assert from 'assert/strict'; + +assert.match('I will fail', /pass/); +// AssertionError [ERR_ASSERTION]: The input did not match the regular ... + +assert.match(123, /pass/); +// AssertionError [ERR_ASSERTION]: The "string" argument must be of type string. + +assert.match('I will pass', /pass/); +// OK +``` + +```cjs +const assert = require('assert/strict'); assert.match('I will fail', /pass/); // AssertionError [ERR_ASSERTION]: The input did not match the regular ... @@ -1034,7 +1489,40 @@ An alias of [`assert.notDeepStrictEqual()`][]. Tests for any deep inequality. Opposite of [`assert.deepEqual()`][]. -```js +```mjs +import assert from 'assert'; + +const obj1 = { + a: { + b: 1 + } +}; +const obj2 = { + a: { + b: 2 + } +}; +const obj3 = { + a: { + b: 1 + } +}; +const obj4 = Object.create(obj1); + +assert.notDeepEqual(obj1, obj1); +// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } } + +assert.notDeepEqual(obj1, obj2); +// OK + +assert.notDeepEqual(obj1, obj3); +// AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } } + +assert.notDeepEqual(obj1, obj4); +// OK +``` + +```cjs const assert = require('assert'); const obj1 = { @@ -1112,8 +1600,15 @@ changes: Tests for deep strict inequality. Opposite of [`assert.deepStrictEqual()`][]. -```js -const assert = require('assert').strict; +```mjs +import assert from 'assert/strict'; + +assert.notDeepStrictEqual({ a: 1 }, { a: '1' }); +// OK +``` + +```cjs +const assert = require('assert/strict'); assert.notDeepStrictEqual({ a: 1 }, { a: '1' }); // OK @@ -1151,7 +1646,20 @@ Tests shallow, coercive inequality with the [Abstract Equality Comparison][] (`!=` ). `NaN` is special handled and treated as being identical in case both sides are `NaN`. -```js +```mjs +import assert from 'assert'; + +assert.notEqual(1, 2); +// OK + +assert.notEqual(1, 1); +// AssertionError: 1 != 1 + +assert.notEqual(1, '1'); +// AssertionError: 1 != '1' +``` + +```cjs const assert = require('assert'); assert.notEqual(1, 2); @@ -1186,8 +1694,23 @@ changes: Tests strict inequality between the `actual` and `expected` parameters as determined by the [SameValue Comparison][]. -```js -const assert = require('assert').strict; +```mjs +import assert from 'assert/strict'; + +assert.notStrictEqual(1, 2); +// OK + +assert.notStrictEqual(1, 1); +// AssertionError [ERR_ASSERTION]: Expected "actual" to be strictly unequal to: +// +// 1 + +assert.notStrictEqual(1, '1'); +// OK +``` + +```cjs +const assert = require('assert/strict'); assert.notStrictEqual(1, 2); // OK @@ -1234,8 +1757,8 @@ If no arguments are passed in at all `message` will be set to the string: Be aware that in the `repl` the error message will be different to the one thrown in a file! See below for further details. -```js -const assert = require('assert').strict; +```mjs +import assert from 'assert/strict'; assert.ok(true); // OK @@ -1267,6 +1790,55 @@ assert.ok(0); // AssertionError: The expression evaluated to a falsy value: // // assert.ok(0) +``` + +```cjs +const assert = require('assert/strict'); + +assert.ok(true); +// OK +assert.ok(1); +// OK + +assert.ok(); +// AssertionError: No value argument passed to `assert.ok()` + +assert.ok(false, 'it\'s false'); +// AssertionError: it's false + +// In the repl: +assert.ok(typeof 123 === 'string'); +// AssertionError: false == true + +// In a file (e.g. test.js): +assert.ok(typeof 123 === 'string'); +// AssertionError: The expression evaluated to a falsy value: +// +// assert.ok(typeof 123 === 'string') + +assert.ok(false); +// AssertionError: The expression evaluated to a falsy value: +// +// assert.ok(false) + +assert.ok(0); +// AssertionError: The expression evaluated to a falsy value: +// +// assert.ok(0) +``` + +```mjs +import assert from 'assert/strict'; + +// Using `assert()` works the same: +assert(0); +// AssertionError: The expression evaluated to a falsy value: +// +// assert(0) +``` + +```cjs +const assert = require('assert'); // Using `assert()` works the same: assert(0); @@ -1305,7 +1877,23 @@ 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 `asyncFn` fails to reject. -```js +```mjs +import assert from 'assert/strict'; + +await assert.rejects( + async () => { + throw new TypeError('Wrong value'); + }, + { + name: 'TypeError', + message: 'Wrong value' + } +); +``` + +```cjs +const assert = require('assert/strict'); + (async () => { await assert.rejects( async () => { @@ -1319,7 +1907,24 @@ if the `asyncFn` fails to reject. })(); ``` -```js +```mjs +import assert from 'assert/strict'; + +await assert.rejects( + async () => { + throw new TypeError('Wrong value'); + }, + (err) => { + assert.strictEqual(err.name, 'TypeError'); + assert.strictEqual(err.message, 'Wrong value'); + return true; + } +); +``` + +```cjs +const assert = require('assert/strict'); + (async () => { await assert.rejects( async () => { @@ -1334,7 +1939,20 @@ if the `asyncFn` fails to reject. })(); ``` -```js +```mjs +import assert from 'assert/strict'; + +assert.rejects( + Promise.reject(new Error('Wrong value')), + Error +).then(() => { + // ... +}); +``` + +```cjs +const asssert = require('assert/strict'); + assert.rejects( Promise.reject(new Error('Wrong value')), Error @@ -1365,8 +1983,36 @@ changes: Tests strict equality between the `actual` and `expected` parameters as determined by the [SameValue Comparison][]. -```js -const assert = require('assert').strict; +```mjs +import assert from 'assert/strict'; + +assert.strictEqual(1, 2); +// AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal: +// +// 1 !== 2 + +assert.strictEqual(1, 1); +// OK + +assert.strictEqual('Hello foobar', 'Hello World!'); +// AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal: +// + actual - expected +// +// + 'Hello foobar' +// - 'Hello World!' +// ^ + +const apples = 1; +const oranges = 2; +assert.strictEqual(apples, oranges, `apples ${apples} !== oranges ${oranges}`); +// AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2 + +assert.strictEqual(1, '1', new TypeError('Inputs are not identical')); +// TypeError: Inputs are not identical +``` + +```cjs +const assert = require('assert/strict'); assert.strictEqual(1, 2); // AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal: @@ -1434,7 +2080,9 @@ fails. Custom validation object/error instance: -```js +```mjs +import assert from 'assert/strict'; + const err = new TypeError('Wrong value'); err.code = 404; err.foo = 'bar'; @@ -1462,7 +2110,7 @@ assert.throws( ); // Using regular expressions to validate error properties: -assert.throws( +throws( () => { throw err; }, @@ -1486,7 +2134,76 @@ assert.throws( ); // Fails due to the different `message` and `name` properties: +throws( + () => { + const otherErr = new Error('Not found'); + // Copy all enumerable properties from `err` to `otherErr`. + for (const [key, value] of Object.entries(err)) { + otherErr[key] = value; + } + throw otherErr; + }, + // The error's `message` and `name` properties will also be checked when using + // an error as validation object. + err +); +``` + +```cjs +const assert = require('assert/strict'); + +const err = new TypeError('Wrong value'); +err.code = 404; +err.foo = 'bar'; +err.info = { + nested: true, + baz: 'text' +}; +err.reg = /abc/i; + assert.throws( + () => { + throw err; + }, + { + name: 'TypeError', + message: 'Wrong value', + info: { + nested: true, + baz: 'text' + } + // Only properties on the validation object will be tested for. + // Using nested objects requires all properties to be present. Otherwise + // the validation is going to fail. + } +); + +// Using regular expressions to validate error properties: +throws( + () => { + throw err; + }, + { + // The `name` and `message` properties are strings and using regular + // expressions on those will match against the string. If they fail, an + // error is thrown. + name: /^TypeError$/, + message: /Wrong/, + foo: 'bar', + info: { + nested: true, + // It is not possible to use regular expressions for nested properties! + baz: 'text' + }, + // The `reg` property contains a regular expression and only if the + // validation object contains an identical regular expression, it is going + // to pass. + reg: /abc/i + } +); + +// Fails due to the different `message` and `name` properties: +throws( () => { const otherErr = new Error('Not found'); // Copy all enumerable properties from `err` to `otherErr`. @@ -1503,7 +2220,20 @@ assert.throws( Validate instanceof using constructor: -```js +```mjs +import assert from 'assert/strict'; + +assert.throws( + () => { + throw new Error('Wrong value'); + }, + Error +); +``` + +```cjs +const assert = require('assert/strict'); + assert.throws( () => { throw new Error('Wrong value'); @@ -1517,7 +2247,20 @@ Validate error message using [`RegExp`][]: Using a regular expression runs `.toString` on the error object, and will therefore also include the error name. -```js +```mjs +import assert from 'assert/strict'; + +assert.throws( + () => { + throw new Error('Wrong value'); + }, + /^Error: Wrong value$/ +); +``` + +```cjs +const assert = require('assert/strict'); + assert.throws( () => { throw new Error('Wrong value'); @@ -1531,7 +2274,30 @@ Custom error validation: The function must return `true` to indicate all internal validations passed. It will otherwise fail with an [`AssertionError`][]. -```js +```mjs +import assert from 'assert/strict'; + +assert.throws( + () => { + throw new Error('Wrong value'); + }, + (err) => { + assert(err instanceof Error); + assert(/value/.test(err)); + // Avoid returning anything from validation functions besides `true`. + // Otherwise, 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' +); +``` + +```cjs +const assert = require('assert/strict'); + assert.throws( () => { throw new Error('Wrong value'); @@ -1558,7 +2324,46 @@ message as the thrown error message is going to result in an a string as the second argument gets considered: -```js +```mjs +import assert from 'assert/strict'; + +function throwingFirst() { + throw new Error('First'); +} + +function throwingSecond() { + throw new Error('Second'); +} + +function notThrowing() {} + +// The second argument is a string and the input function threw an Error. +// 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 throws an `ERR_AMBIGUOUS_ARGUMENT` error. +assert.throws(throwingSecond, 'Second'); +// TypeError [ERR_AMBIGUOUS_ARGUMENT] + +// The string is only used (as message) in case the function does not throw: +assert.throws(notThrowing, 'Second'); +// AssertionError [ERR_ASSERTION]: Missing expected exception: Second + +// If it was intended to match for the error message do this instead: +// It does not throw because the error messages match. +assert.throws(throwingSecond, /Second$/); + +// If the error message does not match, an AssertionError is thrown. +assert.throws(throwingFirst, /Second$/); +// AssertionError [ERR_ASSERTION] +``` + + +```cjs +const assert = require('assert/strict'); + function throwingFirst() { throw new Error('First'); }