Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
[test] Replace waitFor() with act() #14851
[test] Replace waitFor() with act() #14851
Changes from 4 commits
64e41d0
b575fa0
d0b1687
9ac8dba
b1f68d0
191aa84
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
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 clarify why this has been added here? 🤔
There does not seem to be any async action within this
act
. 🤷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.
@LukasTy I used to think the same as you: How can this be any different? But I have noticed behavior changes.
If I remove the
async
in https://github.com/mui/base-ui/blob/463d61a3befa8676e564db695eee2ef645060cec/packages/mui-base/src/Tooltip/Root/TooltipRoot.test.tsx#L103 , it crashes the test, with this error:That error is coming from: https://github.com/facebook/react/blob/1460d67c5b9a0d4498b4d22e1a5a6c0ccac85fdd/packages/react-reconciler/src/ReactFiberAct.js#L54
I have seen the same in testing-library/react-testing-library#1061 (comment).
Looking at the source, I don't quite understand why: https://github.com/facebook/react/blob/1460d67c5b9a0d4498b4d22e1a5a6c0ccac85fdd/packages/react/src/ReactAct.js#L109. Maybe it's because we use a fake clock.
@Janpot A preference on this? You flagged this as wrong in testing-library/eslint-plugin-testing-library#915
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.
Nice. This means that I've been fighting with this error for a bit as well. 🤯 🙈
Adding
async
in theawait act
also helps me avoid the flaky error in my cases. 👍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.
My preference is to follow what React suggests, which is to always use the async version as they plan to deprecate the sync version. That's why I opened that feature request. I noticed while fixing
act
calls in tests (e.g. remove ones that should be unnecessary and wrap calls that should be wrapped) that it magically made tings work more predictably 🙂.I somewhat disagree with this statement though. I believe the primary reason (not the only) for having tests is to have a codebase that is optimized for refactoring. The most useful tests in that mindset are the ones that don't need an update when implementation changes.
act
makes tests rely on implementation details andwaitFor
takes those assumptions away. Not to say that you can't have any tests that test implementation details, just the bulk are testing behavior and the closer you can get your test to real user interaction, the more useful they become (real user events, wait for a11y selector, screenshots,...). Most of the time comes at a cost though 🙂.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.
@Janpot Agree, I opened mui/material-ui#44028 and mui/base-ui#706 for this reason. But I was wondering about
I agree with this. My assumption is that for
act()
to not be enough and having to needwaitFor()
, it must mean that we are doing something creative, in 90+% of the time, it shouldn't be needed. I would expect, as a developer, to be able to take a component from MUI and write a test with prop changes or act() but withoutwaitFor()
.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 see where you're coming from, but I expect the opposite. The way I see it is that the only reason this works is because we execute those events synchronously. Which is quite far from real world usage. To bring the test closer to real user behavior you'd have to think about it as if you were interacting with the page through the event loop. i.e. executing the
.click()
only schedules an event on the event loop and then returns, instead the current behavior as if we're actually in control of when the event fires. If that were the behavior, the only way to test whether a change happened is throughwaitFor
. This is much closer to how you'd interact with a real browser, e.g. over the devtools protocol, and therefore much closer to real user interaction.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.
@Janpot If these were e2e tests, yes, I would expect the same. But those are not e2e tests: we don't fetch data from the network, we don't do real event interactions, so I expect deterministic 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.
But it's
act
that introduces the non-determinism. Tests randomly break on patch releases of dependencies, when the implementation slightly changes, when running them in browsers vs. node, Whenreact
updates, when passing it an async vs sync function,... I fail to see what this notion of deterministic behavior do for us.It's not because we use user-event and
.waitFor
that our tests becomes an e2e-tests. I don't feel like there is much to gain from rigorously categorizing tests this way anyway. It's still a component as a unit that's under test. And it's still testing the exact same behavior. But what it's not doing is alter React behavior in a way that never occurs in the real world. And fire events in isolation that would never fire in isolation in the real world.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.
Ah, then
act()
is not doing its job 😀. It didn't used to be like this. It would reliably work.For me, it's about, after doing an action, knowing that the state if reflected in the DOM without having to think about a complex selector for it (waitFor), as a developer, I want to use one that will always work (act). Then, I can console.log, inspect the DOM, etc. It will always show me up-to-date information, no stale state.