-
-
Notifications
You must be signed in to change notification settings - Fork 3k
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
have Base generate the strings for error messages immediately #3075
Conversation
… of delaying until epilogue, the reason for this is that it is possible to reference objects in errors that could be mutated after the error occurs, causing the printed error message to be misleading
… this is correct behavior
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.
@abrady0 Thanks for the detailed explanation. Hopefully you can entertain my questions...
- Do you have a non-contrived use case where this is a problem? It's unusual to do something like the example.
- Can you speak to
Object.freeze()
as a potential solution?
I may have more comments/questions later, but this is just top of mind.
Hey @boneskull! This popped up in one of our unit tests that was exercising some code that accumulated responses from several different async sources and then processed it. for the suite we initialized a single context to be used by multiple tests (as context initialization has some cost), and simulated the responses to test the handling, clearing the accumulator after each test. Object.freeze is potentially problematic because it could break any existing tests that rely on mutating objects after a test fails, and maybe also because if we want the state of the object to match exactly it would have to be a recursive freeze which could be time consuming if the object was especially large. (I think the first reason is the more legit one, if you're already doing a huge compare the cost of freezing recursively on fail probably won't be a factor). Agree that this is unusual, perhaps suggesting that the freeze approach is better? I'm open either way. Sorry for the late reply! |
I'm going to have to take a closer look at this. It seems OK, but in my experience, those are always the ones that bite me in the ass. 😉 |
I can find no obvious slowdown or extra memory pressure when run against Babel (Mocha, Chai) nor TypeScript (just Mocha), or Chai itself. LGTM |
* master: refactor "forbid pending" tests for DRY add diff tool artifacts to .gitignore [ci skip] fix inaccurate diff output due to post-assertion object mutation (#3075) remove unused code in ms module # Conflicts: # .gitignore
…hajs#3075) * have Base generate the strings for error messages immediately instead of delaying until epilogue, the reason for this is that it is possible to reference objects in errors that could be mutated after the error occurs, causing the printed error message to be misleading * re-add stringify during list to appease tests, open for discussion if this is correct behavior
instead of delaying until epilogue
Requirements
Description of the Change
An object referred to by a test failure can be mutated between the point of test failing and when the object is converted to a string and output. For instance if an
afterEach
clears the fields in an object pointed to by achai.expect.eql
AssertionError'sexpected
oractual
fields then the reporter's error message will be misleadingsee https://codepen.io/abrady0/pen/pWGBxR?editors=1111 for running example.
Here is a snippet that assumes 'spec' reporter:
Alternate Designs
To fix this in chai would require either cloning or stringifying the
actual
andexpected
objects at the time of error creation.Cloning the objects is a potentially expensive, and not always possible operation that would put new constraints on what could be used in an
eql
comparison.Stringifying is a possibility as that is an existing requirement for using the Base reporter, however mocha has its own stringifying function with its own configuration and behavior (such as sorting fields in an object) and would require that chai, and any other libraries similar to chai, import mocha and use
mocha.utils.stringify
(or perhaps another stringifier depending on the reporter), which feels like it breaks modularity pretty hardEven if this was fixed in the chai module, other modules would have to take on the burden of making sure their
actual
andexpected
objects were in mocha compatible string form.Why should this be in core?
This functionality directly affects the Base reporter.
Benefits
The benefit realized by this code change is correct error messages in situations where objects that can be mutated after changing are passed to the Base reporter
Possible Drawbacks
Code that would have executed later is now run when a test fails. This puts an extra burden on memory requirements at the time of test failure and could possibly result in running out of memory space in a tightly constrained test when it fails.
The cost of execution should be the same, but any time you change when things happen it risks having side effects. It is possible the configuration of the reporter is changed in some way that would make the output different before and after this change.
Possibly the string representation of an object is larger than its non-string representation and causes too much memory to be used over the lifetime of the test.
Unlikely but there might be side effects from having the
expected
andactual
no longer referenced such that they get garbage collected at a different time (and maybe are referenced by a weakmap? I'm reaching here)It is possible this bug will appear again in new reporters depending on if they build on the Base reporter or not.
Applicable issues
I didn't make an issue for the fix, just noticed it today in a test and thought I'd take a shot at fixing it.
This is just a bug fix