Update: More detailed assert message for rule-tester#9769
Update: More detailed assert message for rule-tester#9769ilyavolodin merged 4 commits intoeslint:masterfrom
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! |
platinumazure
left a comment
There was a problem hiding this comment.
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. |
| actual: e.actual, | ||
| expected: e.expected, | ||
| operator: e.operator | ||
| }); |
There was a problem hiding this comment.
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.
@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.
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. |
platinumazure
left a comment
There was a problem hiding this comment.
LGTM, just one small thing but not a blocker at all. Thanks for adding a test, as well.
| it = null; | ||
| describe = null; | ||
|
|
||
| const ruleTester = new RuleTester(); |
There was a problem hiding this comment.
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.
I've added the try/finally : )
fa2a5d3 to
4160e75
Compare
4160e75 to
a3f60f0
Compare
platinumazure
left a comment
There was a problem hiding this comment.
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).
| 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.
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.
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.
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. 😀 |
platinumazure
left a comment
There was a problem hiding this comment.
LGTM except for one small issue with the exception message. Thanks!
| 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.
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.
Thanks for review. I've added the parentheses :-)

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.