Skip to content

Commit d3f79aa

Browse files
authored
assert: allow printf-style messages as assertion error
Also add functions as allowed message input. This allows to have leavy message computation to become cheaper. PR-URL: #58849 Reviewed-By: Marco Ippolito <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Rafael Gonzaga <[email protected]>
1 parent 8096aea commit d3f79aa

File tree

6 files changed

+433
-77
lines changed

6 files changed

+433
-77
lines changed

doc/api/assert.md

Lines changed: 97 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,27 @@ strict methods. For example, [`assert.deepEqual()`][] will behave like
3939
In strict assertion mode, error messages for objects display a diff. In legacy
4040
assertion mode, error messages for objects display the objects, often truncated.
4141

42+
### Message parameter semantics
43+
44+
For assertion methods that accept an optional `message` parameter, the message
45+
may be provided in one of the following forms:
46+
47+
* **string**: Used as-is. If additional arguments are supplied after the
48+
`message` string, they are treated as printf-like substitutions (see
49+
[`util.format()`][]).
50+
* **Error**: If an `Error` instance is provided as `message`, that error is
51+
thrown directly instead of an `AssertionError`.
52+
* **function**: A function of the form `(actual, expected) => string`. It is
53+
called only when the assertion fails and should return a string to be used as
54+
the error message. Non-string return values are ignored and the default
55+
message is used instead.
56+
57+
If additional arguments are passed along with an `Error` or a function as
58+
`message`, the call is rejected with `ERR_AMBIGUOUS_ARGUMENT`.
59+
60+
If the first item is neither a string, `Error`, nor function, `ERR_INVALID_ARG_TYPE`
61+
is thrown.
62+
4263
To use strict assertion mode:
4364

4465
```mjs
@@ -305,10 +326,14 @@ destructuring and call methods directly on the instance.
305326

306327
<!-- YAML
307328
added: v0.5.9
329+
changes:
330+
- version: REPLACEME
331+
pr-url: https://github.com/nodejs/node/pull/58849
332+
description: Message may now be a `printf`-like format string or function.
308333
-->
309334

310335
* `value` {any} The input that is checked for being truthy.
311-
* `message` {string|Error}
336+
* `message` {string|Error|Function}
312337

313338
An alias of [`assert.ok()`][].
314339

@@ -324,6 +349,9 @@ changes:
324349
- version: v25.0.0
325350
pr-url: https://github.com/nodejs/node/pull/57627
326351
description: Invalid dates are now considered equal.
352+
- version: REPLACEME
353+
pr-url: https://github.com/nodejs/node/pull/58849
354+
description: Message may now be a `printf`-like format string or function.
327355
- version: v24.0.0
328356
pr-url: https://github.com/nodejs/node/pull/57622
329357
description: Recursion now stops when either side encounters a circular
@@ -375,7 +403,7 @@ changes:
375403

376404
* `actual` {any}
377405
* `expected` {any}
378-
* `message` {string|Error}
406+
* `message` {string|Error|Function}
379407

380408
**Strict assertion mode**
381409

@@ -524,6 +552,9 @@ changes:
524552
- version: v25.0.0
525553
pr-url: https://github.com/nodejs/node/pull/57627
526554
description: Invalid dates are now considered equal.
555+
- version: REPLACEME
556+
pr-url: https://github.com/nodejs/node/pull/58849
557+
description: Message may now be a `printf`-like format string or function.
527558
- version: v24.0.0
528559
pr-url: https://github.com/nodejs/node/pull/57622
529560
description: Recursion now stops when either side encounters a circular
@@ -567,7 +598,7 @@ changes:
567598

568599
* `actual` {any}
569600
* `expected` {any}
570-
* `message` {string|Error}
601+
* `message` {string|Error|Function}
571602

572603
Tests for deep equality between the `actual` and `expected` parameters.
573604
"Deep" equality means that the enumerable "own" properties of child objects
@@ -829,14 +860,17 @@ added:
829860
- v13.6.0
830861
- v12.16.0
831862
changes:
863+
- version: REPLACEME
864+
pr-url: https://github.com/nodejs/node/pull/58849
865+
description: Message may now be a `printf`-like format string or function.
832866
- version: v16.0.0
833867
pr-url: https://github.com/nodejs/node/pull/38111
834868
description: This API is no longer experimental.
835869
-->
836870

837871
* `string` {string}
838872
* `regexp` {RegExp}
839-
* `message` {string|Error}
873+
* `message` {string|Error|Function}
840874

841875
Expects the `string` input not to match the regular expression.
842876

@@ -1069,6 +1103,9 @@ assert.doesNotThrow(
10691103
<!-- YAML
10701104
added: v0.1.21
10711105
changes:
1106+
- version: REPLACEME
1107+
pr-url: https://github.com/nodejs/node/pull/58849
1108+
description: Message may now be a `printf`-like format string or function.
10721109
- version:
10731110
- v16.0.0
10741111
- v14.18.0
@@ -1083,7 +1120,7 @@ changes:
10831120

10841121
* `actual` {any}
10851122
* `expected` {any}
1086-
* `message` {string|Error}
1123+
* `message` {string|Error|Function}
10871124

10881125
**Strict assertion mode**
10891126

@@ -1254,14 +1291,17 @@ added:
12541291
- v13.6.0
12551292
- v12.16.0
12561293
changes:
1294+
- version: REPLACEME
1295+
pr-url: https://github.com/nodejs/node/pull/58849
1296+
description: Message may now be a `printf`-like format string or function.
12571297
- version: v16.0.0
12581298
pr-url: https://github.com/nodejs/node/pull/38111
12591299
description: This API is no longer experimental.
12601300
-->
12611301

12621302
* `string` {string}
12631303
* `regexp` {RegExp}
1264-
* `message` {string|Error}
1304+
* `message` {string|Error|Function}
12651305

12661306
Expects the `string` input to match the regular expression.
12671307

@@ -1303,6 +1343,9 @@ instance of {Error} then it will be thrown instead of the
13031343
<!-- YAML
13041344
added: v0.1.21
13051345
changes:
1346+
- version: REPLACEME
1347+
pr-url: https://github.com/nodejs/node/pull/58849
1348+
description: Message may now be a `printf`-like format string or function.
13061349
- version:
13071350
- v16.0.0
13081351
- v14.18.0
@@ -1338,7 +1381,7 @@ changes:
13381381

13391382
* `actual` {any}
13401383
* `expected` {any}
1341-
* `message` {string|Error}
1384+
* `message` {string|Error|Function}
13421385

13431386
**Strict assertion mode**
13441387

@@ -1427,6 +1470,9 @@ instead of the `AssertionError`.
14271470
<!-- YAML
14281471
added: v1.2.0
14291472
changes:
1473+
- version: REPLACEME
1474+
pr-url: https://github.com/nodejs/node/pull/58849
1475+
description: Message may now be a `printf`-like format string or function.
14301476
- version: v9.0.0
14311477
pr-url: https://github.com/nodejs/node/pull/15398
14321478
description: The `-0` and `+0` are not considered equal anymore.
@@ -1458,7 +1504,7 @@ changes:
14581504

14591505
* `actual` {any}
14601506
* `expected` {any}
1461-
* `message` {string|Error}
1507+
* `message` {string|Error|Function}
14621508

14631509
Tests for deep strict inequality. Opposite of [`assert.deepStrictEqual()`][].
14641510

@@ -1487,6 +1533,9 @@ instead of the [`AssertionError`][].
14871533
<!-- YAML
14881534
added: v0.1.21
14891535
changes:
1536+
- version: REPLACEME
1537+
pr-url: https://github.com/nodejs/node/pull/58849
1538+
description: Message may now be a `printf`-like format string or function.
14901539
- version:
14911540
- v16.0.0
14921541
- v14.18.0
@@ -1501,7 +1550,7 @@ changes:
15011550

15021551
* `actual` {any}
15031552
* `expected` {any}
1504-
* `message` {string|Error}
1553+
* `message` {string|Error|Function}
15051554

15061555
**Strict assertion mode**
15071556

@@ -1551,14 +1600,17 @@ parameter is an instance of {Error} then it will be thrown instead of the
15511600
<!-- YAML
15521601
added: v0.1.21
15531602
changes:
1603+
- version: REPLACEME
1604+
pr-url: https://github.com/nodejs/node/pull/58849
1605+
description: Message may now be a `printf`-like format string or function.
15541606
- version: v10.0.0
15551607
pr-url: https://github.com/nodejs/node/pull/17003
15561608
description: Used comparison changed from Strict Equality to `Object.is()`.
15571609
-->
15581610

15591611
* `actual` {any}
15601612
* `expected` {any}
1561-
* `message` {string|Error}
1613+
* `message` {string|Error|Function}
15621614

15631615
Tests strict inequality between the `actual` and `expected` parameters as
15641616
determined by [`Object.is()`][].
@@ -1604,14 +1656,17 @@ instead of the `AssertionError`.
16041656
<!-- YAML
16051657
added: v0.1.21
16061658
changes:
1659+
- version: REPLACEME
1660+
pr-url: https://github.com/nodejs/node/pull/58849
1661+
description: Message may now be a `printf`-like format string or function.
16071662
- version: v10.0.0
16081663
pr-url: https://github.com/nodejs/node/pull/18319
16091664
description: The `assert.ok()` (no arguments) will now use a predefined
16101665
error message.
16111666
-->
16121667

16131668
* `value` {any}
1614-
* `message` {string|Error}
1669+
* `message` {string|Error|Function}
16151670

16161671
Tests if `value` is truthy. It is equivalent to
16171672
`assert.equal(!!value, true, message)`.
@@ -1844,14 +1899,24 @@ argument gets considered.
18441899
<!-- YAML
18451900
added: v0.1.21
18461901
changes:
1902+
- version: REPLACEME
1903+
pr-url: https://github.com/nodejs/node/pull/58849
1904+
description: Message may now be a `printf`-like format string or function.
18471905
- version: v10.0.0
18481906
pr-url: https://github.com/nodejs/node/pull/17003
18491907
description: Used comparison changed from Strict Equality to `Object.is()`.
18501908
-->
18511909

18521910
* `actual` {any}
18531911
* `expected` {any}
1854-
* `message` {string|Error}
1912+
* `message` {string|Error|Function} Postfix `printf`-like arguments in case
1913+
it's used as format string.
1914+
If message is a function, it is called in case of a comparison failure. The
1915+
function receives the `actual` and `expected` arguments and has to return a
1916+
string that is going to be used as error message.
1917+
`printf`-like format strings and functions are beneficial for performance
1918+
reasons in case arguments are passed through. In addition, it allows nice
1919+
formatting with ease.
18551920

18561921
Tests strict equality between the `actual` and `expected` parameters as
18571922
determined by [`Object.is()`][].
@@ -1880,8 +1945,17 @@ const oranges = 2;
18801945
assert.strictEqual(apples, oranges, `apples ${apples} !== oranges ${oranges}`);
18811946
// AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2
18821947

1948+
assert.strictEqual(apples, oranges, 'apples %s !== oranges %s', apples, oranges);
1949+
// AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2
1950+
18831951
assert.strictEqual(1, '1', new TypeError('Inputs are not identical'));
18841952
// TypeError: Inputs are not identical
1953+
1954+
assert.strictEqual(apples, oranges, (actual, expected) => {
1955+
// Do 'heavy' computations
1956+
return `I expected ${expected} but I got ${actual}`;
1957+
});
1958+
// AssertionError [ERR_ASSERTION]: I expected oranges but I got apples
18851959
```
18861960

18871961
```cjs
@@ -1908,8 +1982,17 @@ const oranges = 2;
19081982
assert.strictEqual(apples, oranges, `apples ${apples} !== oranges ${oranges}`);
19091983
// AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2
19101984

1985+
assert.strictEqual(apples, oranges, 'apples %s !== oranges %s', apples, oranges);
1986+
// AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2
1987+
19111988
assert.strictEqual(1, '1', new TypeError('Inputs are not identical'));
19121989
// TypeError: Inputs are not identical
1990+
1991+
assert.strictEqual(apples, oranges, (actual, expected) => {
1992+
// Do 'heavy' computations
1993+
return `I expected ${expected} but I got ${actual}`;
1994+
});
1995+
// AssertionError [ERR_ASSERTION]: I expected oranges but I got apples
19131996
```
19141997

19151998
If the values are not strictly equal, an [`AssertionError`][] is thrown with a
@@ -2295,7 +2378,7 @@ changes:
22952378

22962379
* `actual` {any}
22972380
* `expected` {any}
2298-
* `message` {string|Error}
2381+
* `message` {string|Error|Function}
22992382

23002383
Tests for partial deep equality between the `actual` and `expected` parameters.
23012384
"Deep" equality means that the enumerable "own" properties of child objects
@@ -2460,5 +2543,6 @@ assert.partialDeepStrictEqual(
24602543
[`assert.strictEqual()`]: #assertstrictequalactual-expected-message
24612544
[`assert.throws()`]: #assertthrowsfn-error-message
24622545
[`getColorDepth()`]: tty.md#writestreamgetcolordepthenv
2546+
[`util.format()`]: util.md#utilformatformat-args
24632547
[enumerable "own" properties]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties
24642548
[prototype-spec]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots

0 commit comments

Comments
 (0)