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
Update: More detailed assert message for rule-tester #9769
Conversation
Thanks for the pull request! This was actually proposed before in #8173. I think the reason you're seeing confusing error messages is because you're using This is because I don't think it would make sense to have the error message change for people using This function gets called when no test runner is active. (The first argument is the name of the test case, and the second argument is a function that may or may not throw an |
549e40a
to
7cfa106
Compare
@not-an-aardvark Thanks for your comment! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add or modify a unit test demonstrating the changes (lack of message, explicitly check for actual/expected/operator, etc.)? Thanks!
@platinumazure Thanks for review. This change is only for the case where no test runner is being used. But our unit tests seem based on some runners (hope I don't miss something), so it actually doesn't break any unit test now. Is there any test for the no-test-runner case to add in? |
Are there any tests for RuleTester.describe and RuleTester.it now, with them being called directly rather than overwritten? If so, it seems like those would be a good place to test. If there aren't tests (possible since the default behavior is to proxy into the method passed in), we should add some. |
lib/testers/rule-tester.js
Outdated
actual: e.actual, | ||
expected: e.expected, | ||
operator: e.operator | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had been envisioning something more like this:
try {
return method.apply(this);
} catch (err) {
if (err instanceof assert.AssertionError) {
throw Object.assign({}, new assert.AssertionError, { message: `${e.actual} ${e.operator} ${e.expected}` })
}
throw err;
}
That way, any unrelated errors (e.g. crashes in a rule) would still get passed through unmodified, and the original message (e.g. "output is incorrect") would still be present for additional context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@not-an-aardvark using a plain object as error object is not the same as throwing a new AssertionError
. Using Object.assign
is also not sufficient because stack
and message
are non-enumerable and would not be copied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, you're right; I had forgotten that the thrown object would no longer be an AssertionError
. I think something like this could work:
try {
return method.apply(this);
} catch (err) {
if (err instanceof assert.AssertionError) {
err.message += `${util.inspect(err.actual)} ${err.operator} ${util.inspect(err.expected)}`;
}
throw err;
}
To me, the important constraints are:
- We don't add redundant info in the case where a test runner is being used (this is by far the most common case)
- The error message still contains information about what the problem is (e.g. it should still say something like "output is incorrect" when the output is incorrect, rather than just displaying a diff between two strings)
@platinumazure I agree that it would be good to have tests like that, although I don't know how we would actually create them. |
You could remove the globals, require |
7cfa106
to
7252351
Compare
Pushed commit to address comment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just one small thing but not a blocker at all. Thanks for adding a test, as well.
tests/lib/testers/no-test-runners.js
Outdated
it = null; | ||
describe = null; | ||
|
||
const ruleTester = new RuleTester(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd feel better if this logic (from here to the end of the assertion) were wrapped in a try/finally so that the globals are definitely reassigned even if an exception is thrown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added the try/finally : )
fa2a5d3
to
4160e75
Compare
4160e75
to
a3f60f0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Note that you could omit the catch clause (try/finally are legal without catch. A try statement just needs at least one catch or finally).
lib/testers/rule-tester.js
Outdated
return method.apply(this); | ||
} catch (err) { | ||
if (err instanceof assert.AssertionError) { | ||
err.message = `${util.inspect(err.actual)} ${err.operator} ${util.inspect(err.expected)}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you make this append to the existing message rather than overwriting it? I think it would be good to retain the "Output is incorrect"
text to make the source of the error clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I forgot to explain here.
The defaultHandler
is called nestedly somehow (sorry I don't dive into the code but its behaviour shows that), so the thrown error will be caught and err message
will be appended in multiple times. It will finally become like
"Output is incorrect 'a' == 'b' 'a' == 'b' 'a' == 'b'"
That's the reason for overwriting it here.
Another solution is adding a flag like appended
on error object to avoid multiple appending. But is it worth to do it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see. I think that's happening because defaultHandler
is used for both the "describe" and the "it" methods. So effectively something like this is happening:
defaultHandler("foo", () => {
defaultHandler("bar", () => {
// throw assertion error
});
});
One solution would be to use the old default handler (which just calls the function in the second argument) as the default for describe
, and use the handler that appends a diff as the default for it
.
Actually I have another idea to implement this (I think this is better): This is the code we using now: assert.equal(fixResult.output, item.output, "Output is incorrect."); We can change it into: const message = "Output is incorrect."
const inTestRunner = typeof describe === "function" || typeof it === "function"
if (!inTestRunner) {
// if not using test runner, the assert message should be appended by diff message.
message += ` Expected: ${item.output} Received: ${fixResult.output}`
}
assert.equal(fixResult.output, item.output, message); @not-an-aardvark What do you think about this? : ) |
I think a flag (maybe even a symbol property?) would be better here. If I
develop a plugin and choose to use a test framework that doesn't export
those globals (and I overwrite RuleTester.describe and RuleTester.it), then
the latest suggestion would incorrectly infer I'm never in a test
environment.
Thanks for bearing with us as we keep working towards a solution. I really
appreciate your patience and persistence.
…On Dec 26, 2017 8:13 PM, "Weijia Wang" ***@***.***> wrote:
Actually I have another idea to implement this (I think this is better):
This is the code we using now:
assert.equal(fixResult.output, item.output, "Output is incorrect.");
We can change it into:
const errMsg = "Output is incorrect."const inTestRunner = typeof describe === "function" || typeof it === "function"if (!inTestRunner) {
// if not using test runner, the error message should be appended by diff message.
errMsg += ` Expected: ${item.output} Received: ${fixResult.output}`
}assert.equal(fixResult.output, item.output, errMsg);
@not-an-aardvark <https://github.com/not-an-aardvark> What do you think
about this? : )
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#9769 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AARWerYekjDKrEH3mmfjuEalA5zSx3Shks5tEae4gaJpZM4RMKX_>
.
|
Updated PR to address comment. Please take another look. 😀 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM except for one small issue with the exception message. Thanks!
lib/testers/rule-tester.js
Outdated
return method.apply(this); | ||
} catch (err) { | ||
if (err instanceof assert.AssertionError) { | ||
err.message += ` ${util.inspect(err.actual)} ${err.operator} ${util.inspect(err.expected)}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think surrounding the comparison in parentheses would be useful and make the error message more readable: (${util.inspect(err.actual)} ${err.operator} ${util.inspect(err.expected)})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for review. I've added the parentheses :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for PR
What is the purpose of this pull request? (put an "X" next to item)
[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[ ] Add autofixing to a rule
[ ] Add a CLI option
[X] Add something to the core
[ ] Other, please explain:
What changes did you make? (Give an overview)
The assert message now is a little bit confused. It only tells you "
Output is incorrect.
", but doesn't specificly show the received value and the expected value.This change is to make the assert message more detailed.
Is there anything you'd like reviewers to focus on?
Not yet.