Skip to content

Commit a1c96f8

Browse files
committed
assert: improve assert.throws
This switches the assert.throws output to the one used in strict mode if a error object is used for comparison. From now on it will show the complete difference between two objects instead of only showing the first failing property. It also fixes detecting properties with a undefined value and fails in case the thrown error does not contain the value at all. PR-URL: #19463 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 28e4e43 commit a1c96f8

File tree

5 files changed

+80
-45
lines changed

5 files changed

+80
-45
lines changed

lib/assert.js

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -366,13 +366,38 @@ assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
366366
}
367367
};
368368

369-
function compareExceptionKey(actual, expected, key, msg) {
370-
if (!isDeepStrictEqual(actual[key], expected[key])) {
369+
class Comparison {
370+
constructor(obj, keys) {
371+
for (const key of keys) {
372+
if (key in obj)
373+
this[key] = obj[key];
374+
}
375+
}
376+
}
377+
378+
function compareExceptionKey(actual, expected, key, message, keys) {
379+
if (!(key in actual) || !isDeepStrictEqual(actual[key], expected[key])) {
380+
if (!message) {
381+
// Create placeholder objects to create a nice output.
382+
const a = new Comparison(actual, keys);
383+
const b = new Comparison(expected, keys);
384+
385+
const tmpLimit = Error.stackTraceLimit;
386+
Error.stackTraceLimit = 0;
387+
const err = new AssertionError({
388+
actual: a,
389+
expected: b,
390+
operator: 'deepStrictEqual',
391+
stackStartFn: assert.throws,
392+
errorDiff: ERR_DIFF_EQUAL
393+
});
394+
Error.stackTraceLimit = tmpLimit;
395+
message = err.message;
396+
}
371397
innerFail({
372-
actual: actual[key],
373-
expected: expected[key],
374-
message: msg || `${key}: expected ${inspect(expected[key])}, ` +
375-
`not ${inspect(actual[key])}`,
398+
actual,
399+
expected,
400+
message,
376401
operator: 'throws',
377402
stackStartFn: assert.throws
378403
});
@@ -389,16 +414,14 @@ function expectedException(actual, expected, msg) {
389414
'expected', ['Function', 'RegExp'], expected
390415
);
391416
}
392-
// The name and message could be non enumerable. Therefore test them
393-
// explicitly.
394-
if ('name' in expected) {
395-
compareExceptionKey(actual, expected, 'name', msg);
396-
}
397-
if ('message' in expected) {
398-
compareExceptionKey(actual, expected, 'message', msg);
417+
const keys = Object.keys(expected);
418+
// Special handle errors to make sure the name and the message are compared
419+
// as well.
420+
if (expected instanceof Error) {
421+
keys.push('name', 'message');
399422
}
400-
for (const key of Object.keys(expected)) {
401-
compareExceptionKey(actual, expected, key, msg);
423+
for (const key of keys) {
424+
compareExceptionKey(actual, expected, key, msg, keys);
402425
}
403426
return true;
404427
}

test/message/assert_throws_stack.out

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@ assert.js:*
22
throw new AssertionError(obj);
33
^
44

5-
AssertionError [ERR_ASSERTION]: bar: expected true, not undefined
5+
AssertionError [ERR_ASSERTION]: Input A expected to deepStrictEqual input B:
6+
+ expected - actual
7+
8+
- Comparison {}
9+
+ Comparison {
10+
+ bar: true
11+
+ }
612
at Object.<anonymous> (*assert_throws_stack.js:*:*)
713
at *
814
at *

test/parallel/test-assert-fail-deprecation.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,7 @@ assert.throws(() => {
3838
assert.fail(typeof 1, 'object', new TypeError('another custom message'));
3939
}, {
4040
name: 'TypeError',
41-
message: 'another custom message',
42-
operator: undefined,
43-
actual: undefined,
44-
expected: undefined,
45-
code: undefined
41+
message: 'another custom message'
4642
});
4743

4844
// No third arg (but a fourth arg)

test/parallel/test-assert-fail.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,5 @@ assert.throws(() => {
3333
assert.fail(new TypeError('custom message'));
3434
}, {
3535
name: 'TypeError',
36-
message: 'custom message',
37-
operator: undefined,
38-
actual: undefined,
39-
expected: undefined
36+
message: 'custom message'
4037
});

test/parallel/test-assert.js

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ const { writeFileSync, unlinkSync } = require('fs');
3434
const { inspect } = require('util');
3535
const a = assert;
3636

37+
const colors = process.stdout.isTTY && process.stdout.getColorDepth() > 1;
38+
const start = 'Input A expected to deepStrictEqual input B:';
39+
const actExp = colors ?
40+
'\u001b[32m+ expected\u001b[39m \u001b[31m- actual\u001b[39m' :
41+
'+ expected - actual';
42+
3743
assert.ok(a.AssertionError.prototype instanceof Error,
3844
'a.AssertionError instanceof Error');
3945

@@ -436,11 +442,6 @@ common.expectsError(
436442
Error.stackTraceLimit = tmpLimit;
437443

438444
// Test error diffs.
439-
const colors = process.stdout.isTTY && process.stdout.getColorDepth() > 1;
440-
const start = 'Input A expected to deepStrictEqual input B:';
441-
const actExp = colors ?
442-
'\u001b[32m+ expected\u001b[39m \u001b[31m- actual\u001b[39m' :
443-
'+ expected - actual';
444445
const plus = colors ? '\u001b[32m+\u001b[39m' : '+';
445446
const minus = colors ? '\u001b[31m-\u001b[39m' : '-';
446447
let message = [
@@ -765,24 +766,32 @@ common.expectsError(
765766
errObj.code = 404;
766767
assert.throws(errFn, errObj);
767768

768-
errObj.code = '404';
769-
common.expectsError(
769+
// Fail in case a expected property is undefined and not existent on the
770+
// error.
771+
errObj.foo = undefined;
772+
assert.throws(
770773
() => assert.throws(errFn, errObj),
771774
{
772775
code: 'ERR_ASSERTION',
773-
type: assert.AssertionError,
774-
message: 'code: expected \'404\', not 404'
776+
name: 'AssertionError [ERR_ASSERTION]',
777+
message: `${start}\n${actExp}\n\n` +
778+
" Comparison {\n name: 'TypeError',\n" +
779+
" message: 'Wrong value',\n- code: 404\n" +
780+
'+ code: 404,\n+ foo: undefined\n }'
775781
}
776782
);
777783

778-
errObj.code = 404;
779-
errObj.foo = 'bar';
780-
common.expectsError(
784+
// Show multiple wrong properties at the same time.
785+
errObj.code = '404';
786+
assert.throws(
781787
() => assert.throws(errFn, errObj),
782788
{
783789
code: 'ERR_ASSERTION',
784-
type: assert.AssertionError,
785-
message: 'foo: expected \'bar\', not undefined'
790+
name: 'AssertionError [ERR_ASSERTION]',
791+
message: `${start}\n${actExp}\n\n` +
792+
" Comparison {\n name: 'TypeError',\n" +
793+
" message: 'Wrong value',\n- code: 404\n" +
794+
"+ code: '404',\n+ foo: undefined\n }"
786795
}
787796
);
788797

@@ -806,20 +815,24 @@ common.expectsError(
806815
);
807816

808817
assert.throws(() => { throw new Error('e'); }, new Error('e'));
809-
common.expectsError(
818+
assert.throws(
810819
() => assert.throws(() => { throw new TypeError('e'); }, new Error('e')),
811820
{
812-
type: assert.AssertionError,
821+
name: 'AssertionError [ERR_ASSERTION]',
813822
code: 'ERR_ASSERTION',
814-
message: "name: expected 'Error', not 'TypeError'"
823+
message: `${start}\n${actExp}\n\n` +
824+
" Comparison {\n- name: 'TypeError',\n+ name: 'Error'," +
825+
"\n message: 'e'\n }"
815826
}
816827
);
817-
common.expectsError(
828+
assert.throws(
818829
() => assert.throws(() => { throw new Error('foo'); }, new Error('')),
819830
{
820-
type: assert.AssertionError,
831+
name: 'AssertionError [ERR_ASSERTION]',
821832
code: 'ERR_ASSERTION',
822-
message: "message: expected '', not 'foo'"
833+
message: `${start}\n${actExp}\n\n` +
834+
" Comparison {\n name: 'Error',\n- message: 'foo'" +
835+
"\n+ message: ''\n }"
823836
}
824837
);
825838

0 commit comments

Comments
 (0)