Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

assert: improve error messages #19467

Closed
wants to merge 3 commits into from

Conversation

BridgeAR
Copy link
Member

@BridgeAR BridgeAR commented Mar 20, 2018

This switches all assert error messages to use the new diffing output as in the assert strict mode.

It also makes sure not reference equal objects are now visualised as such. Before it would e.g. have been [1, 2] strictEqual [1, 2] and that would be quite confusing. That is now detected and expressed.

I also added support in util.inspect to visualize arguments to distinguish them from a regular object.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

@BridgeAR BridgeAR added the semver-major PRs that contain breaking changes and should be released in the next major version. label Mar 20, 2018
@nodejs-github-bot nodejs-github-bot added assert Issues and PRs related to the assert subsystem. errors Issues and PRs related to JavaScript errors originated in Node.js core. util Issues and PRs related to the built-in util module. labels Mar 20, 2018
@BridgeAR
Copy link
Member Author

BridgeAR commented Mar 20, 2018

I am uncertain why the CI fails. Could someone please take a look?

@BridgeAR
Copy link
Member Author

@BridgeAR
Copy link
Member Author

Seems like this breaks eslint tests. @not-an-aardvark can you PTAL?

It does not seem like a big problem to me but I might be wrong.

Here is a small patch that might fix it (without verifying it for real):

@@ -439,7 +439,7 @@ class RuleTester {
                     `Expected '${actual}' to match ${expected}`
                 );
             } else {
-                assert.strictEqual(actual, expected);
+                assert.strictEqual(actual, expected, util.format("%s strictEqual %s", actual, expected));
             }
         }

Looking at it further, it might make sense to change some more assert things as well like e.g.:

diff --git a/lib/testers/rule-tester.js b/lib/testers/rule-tester.js
index de218a875..798253368 100644
--- a/lib/testers/rule-tester.js
+++ b/lib/testers/rule-tester.js
@@ -396,11 +396,11 @@ class RuleTester {
          * @private
          */
         function assertASTDidntChange(beforeAST, afterAST) {
-            if (!lodash.isEqual(beforeAST, afterAST)) {
-
-                // Not using directly to avoid performance problem in node 6.1.0. See #6111
-                // eslint-disable-next-line no-restricted-properties
-                assert.deepEqual(beforeAST, afterAST, "Rule should not modify AST.");
+            if (util.isDeepStrictEqual) {
+                assert.deepStrictEqual(beforeAST, afterAST, "Rule should not modify AST.");
+                // Not using directly to avoid performance problem in Node.js 6 and 7. See #6111
+            } else if (!lodash.isEqual(beforeAST, afterAST)) {
+                assert.fail("Rule should not modify AST.");
             }
         }
 
@@ -539,7 +539,7 @@ class RuleTester {
                     } else {
 
                         // Message was an unexpected type
-                        assert.fail(message, null, "Error should be a string, object, or RegExp.");
+                        assert.fail("Error should be a string, object, or RegExp.");
                     }
                 }
             }
@@ -554,8 +554,7 @@ class RuleTester {
                 } else {
                     const fixResult = SourceCodeFixer.applyFixes(item.code, messages);
 
-                    // eslint-disable-next-line no-restricted-properties
-                    assert.equal(fixResult.output, item.output, "Output is incorrect.");
+                    assert.strictEqual(fixResult.output, item.output, "Output is incorrect.");
                 }
             }

@not-an-aardvark
Copy link
Contributor

I agree that the eslint test failure is not a big problem (it looks like no significant functionality is broken).

Presumably, eslint users would also benefit from improved assertion error messages, so I'm not sure it would be a good idea to lock the error messages within eslint just to get the existing tests to pass. I suppose we could do feature-detection in the tests to check what error message to expect, e.g.:

assert.strictEqual(message, new assert.AssertionError({ actual: 5, expected: 'foo', operator: '===' }).message);

@Trott
Copy link
Member

Trott commented Mar 21, 2018

If we do feature detection, maybe include a comment about when we can drop the feature detection. (In other words, "this feature detection code won't be needed anymore after we drop support for Node.js 8.0.0" or whatever. That assumes it's possible to know that version number at this time...)

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@BridgeAR
Copy link
Member Author

Rebased due to conflicts.

New CI https://ci.nodejs.org/job/node-test-pull-request/13857/

@BridgeAR BridgeAR force-pushed the improve-error-messages branch 3 times, most recently from 4b06f51 to ec5e5c1 Compare April 2, 2018 22:07
Copy link
Member Author

@BridgeAR BridgeAR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the code (rebased due to conflicts) and fixed some small bugs (e.g. errors with extra properties did not have a proper error message), removed the errorDiff property` and added some proxy tests.

breakLength: Infinity,
// Assert does not detect proxies currently.
showProxy: false
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this is actually relatively important to make sure the errors are actually printed properly, no matter how the default options where changed. Only the color setting is not important.

Object.defineProperty(target, 'message', { value: source.message });
Object.setPrototypeOf(target, Object.getPrototypeOf(source));
return target;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the easiest and best way to pretend to be an error without a stack trace.

notDeepStrictEqual: 'Input A expected to strictly not deep equal input B',
strictEqual: 'Input A expected to strictly equal input B',
notStrictEqual: 'Input A expected to strictly not equal input B'
};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// This code path should not be possible to reach.
// The output is identical but it is not clear why.
base = 'Input objects not identical:';
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has happened before and there might still be some rare cases that I am not aware about that could produce this weird output. I am not sure how to handle that better though.

'stack' in actual && actual instanceof Error &&
'stack' in expected && expected instanceof Error) {
actual = copyError(actual);
expected = copyError(expected);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is best to actually show the stack trace in the comparison in case there is only a single error. That way it is clearer in what way the input is different from each other.

const arrProxy = new Proxy([1, 2], {});
assert.deepStrictEqual(arrProxy, [1, 2]);
const tmp = util.inspect.defaultOptions;
util.inspect.defaultOptions = { showProxy: true };
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any opinions from others about checking for proxies?

@BridgeAR
Copy link
Member Author

BridgeAR commented Apr 2, 2018

@BridgeAR
Copy link
Member Author

BridgeAR commented Apr 2, 2018

@jasnell @mcollina PTAL

@jasnell
Copy link
Member

jasnell commented Apr 2, 2018

Still lgtm

@@ -19,6 +19,13 @@ let green = '';
let red = '';
let white = '';

const READABLE_OPERATOR = {
deepStrictEqual: 'Input A expected to strictly deep equal input B',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe strictly deep-equal?

@@ -19,6 +19,13 @@ let green = '';
let red = '';
let white = '';

const READABLE_OPERATOR = {
deepStrictEqual: 'Input A expected to strictly deep equal input B',
notDeepStrictEqual: 'Input A expected to strictly not deep equal input B',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here? deep equal -> deep-equal?

@Trott
Copy link
Member

Trott commented Apr 2, 2018

Not sure how I feel about Input A and Input B being used for "first argument" and "second argument". Maybe Argument 1 and Argument 2 would be less problematic? (Or even just going full verbose and using first argument and second argument?)

@BridgeAR
Copy link
Member Author

BridgeAR commented Apr 4, 2018

I addressed the comment. @Trott I would like to keep Input A / B for now as that could be changed in a different PR. Are you OK with that, so this can land?

@Trott
Copy link
Member

Trott commented Apr 4, 2018

I addressed the comment. @Trott I would like to keep Input A / B for now as that could be changed in a different PR. Are you OK with that, so this can land?

Yes, that's fine by me.

@BridgeAR BridgeAR requested a review from a team April 4, 2018 17:50
@BridgeAR
Copy link
Member Author

BridgeAR commented Apr 4, 2018

It would be nice to get another LG before landing.

@@ -7,7 +7,7 @@ try {
process.env.COLORTERM = '1';
assert.deepStrictEqual([1, 2], [2, 2]);
} catch (err) {
const expected = 'Input A expected to deepStrictEqual input B:\n' +
const expected = 'Input A expected to strictly deep equal input B:\n' +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above: Shouldn't this fail because it should be expecting deep-equal?

@BridgeAR
Copy link
Member Author

BridgeAR commented Apr 4, 2018

@BridgeAR BridgeAR mentioned this pull request Apr 5, 2018
4 tasks
@BridgeAR
Copy link
Member Author

BridgeAR commented Apr 9, 2018

@nodejs/testing @nodejs/tsc PTAL. I would like to land this soon.

@Trott
Copy link
Member

Trott commented Apr 9, 2018

Since it's semver-major, it needs a CITGM run, if I'm not mistaken.

@BridgeAR
Copy link
Member Author

@Trott I did that already in the beginning. It breaks the eslint tests and I opened a PR in eslint to address that. Besides that there was nothing and that should not be blocking.

@Trott
Copy link
Member

Trott commented Apr 10, 2018

@Trott I did that already in the beginning.

Not sure how I missed that. Sorry!

@BridgeAR
Copy link
Member Author

If there is no objection until then, I am going to land this in ~30 hours.

@BridgeAR BridgeAR requested a review from targos April 12, 2018 16:00
@targos
Copy link
Member

targos commented Apr 12, 2018

No objections from me

@Trott
Copy link
Member

Trott commented Apr 12, 2018

Re-running failed Windows CI: https://ci.nodejs.org/job/node-test-commit-windows-fanned/17190/

Right now it is not possible to distinguish arguments from a regular
object. This adds a arguments indicator.
This improves the error messages for:
- assert.notDeepStrictEqual
- assert.deepStrictEqual
- assert.notStrictEqual
- assert.strictEqual

Those will now always use the same error message as used in the
strict mode.
The property is not necessary as it is possible to check for the
operator instead.
@BridgeAR
Copy link
Member Author

Rebased due to minor conflicts in the tests.

One more CI before landing: https://ci.nodejs.org/job/node-test-pull-request/14266/

@BridgeAR BridgeAR added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Apr 13, 2018
@mcollina
Copy link
Member

@mcollina
Copy link
Member

Still LGTM

@jasnell
Copy link
Member

jasnell commented Apr 14, 2018

@nodejs/tsc ... any objections to this landing in 10.0.0?

jasnell pushed a commit that referenced this pull request Apr 14, 2018
Right now it is not possible to distinguish arguments from a regular
object. This adds a arguments indicator.

PR-URL: #19467
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
jasnell pushed a commit that referenced this pull request Apr 14, 2018
This improves the error messages for:
- assert.notDeepStrictEqual
- assert.deepStrictEqual
- assert.notStrictEqual
- assert.strictEqual

Those will now always use the same error message as used in the
strict mode.

PR-URL: #19467
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
jasnell pushed a commit that referenced this pull request Apr 14, 2018
The property is not necessary as it is possible to check for the
operator instead.

PR-URL: #19467
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
@jasnell
Copy link
Member

jasnell commented Apr 14, 2018

Landed in f2d112c, bfe54df, and 2b0825e

@jasnell jasnell closed this Apr 14, 2018
@mcollina
Copy link
Member

+1 for me

jasnell pushed a commit that referenced this pull request Apr 16, 2018
Right now it is not possible to distinguish arguments from a regular
object. This adds a arguments indicator.

PR-URL: #19467
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
jasnell pushed a commit that referenced this pull request Apr 16, 2018
This improves the error messages for:
- assert.notDeepStrictEqual
- assert.deepStrictEqual
- assert.notStrictEqual
- assert.strictEqual

Those will now always use the same error message as used in the
strict mode.

PR-URL: #19467
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
jasnell pushed a commit that referenced this pull request Apr 16, 2018
The property is not necessary as it is possible to check for the
operator instead.

PR-URL: #19467
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
@BridgeAR BridgeAR deleted the improve-error-messages branch January 20, 2020 11:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
assert Issues and PRs related to the assert subsystem. author ready PRs that have at least one approval, no pending requests for changes, and a CI started. errors Issues and PRs related to JavaScript errors originated in Node.js core. semver-major PRs that contain breaking changes and should be released in the next major version. util Issues and PRs related to the built-in util module.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants