Skip to content

Instantly share code, notes, and snippets.

@luan0ap
Forked from threepointone/answers.md
Created March 8, 2019 18:05
Show Gist options
  • Save luan0ap/de2bf50f8c0a1533e22e34809d82c2b5 to your computer and use it in GitHub Desktop.
Save luan0ap/de2bf50f8c0a1533e22e34809d82c2b5 to your computer and use it in GitHub Desktop.

good doc! some quick answers -

It seems like act() is being recommended for wrapping all state updates in React tests, but is it necessary to use it everywhere if you can use waitForElement to turn the whole test async?

This is a very good question, and something I grappled with earlier. A couple of things that stood out for me -

  • waiting for an element is indeed pretty close to what a user's experience is like; ie - a user 'waits' for the form to show itself, after which they fill it in and click a button, then 'wait' for the success screen etc. Ultimately, act() makes this test stronger - it'll ensure that effects, and queued promises, have been flushed before you interact with the element. wrapping waitForElement with act() (the async version, ie), will make this invisible to the user, but with the guarantee that their UI is 'stable'.

  • I couldn't assume that all tests would use waitForElement. For example, using timers is common for testing transitions and such. In these scenarios too, act() is useful to ensure a stable UI.

Developers need to manually choose how to group updates to resemble batch patterns the app will actually experience - is there some way to "fuzz" this and somehow trigger more batching in JSDOM automatically? Maybe by removing this maximum frame rate check in test mode?

This is just the nature of unit tests for async/"time travel" based stuff. I don't have a particularly good answer here; the 'ideal' method would be to have a whole new language and infra. And maybe an actual time machine haha. What's interesting is act now provides a way to actually batch these actions and surface issues that would not have been apparent in tests before.

Doesn't batching promise updates gloss over some possible states that may be perceptible to the user? For example, an async call in cDM might be mocked by a Promise that resolves in 1 tick in test world, but 0.5s in real life. Squashing those updates seems wrong if there is no way to inspect the intermediate state.

Yes :D But those are usually explicit (and you'll note you have this same problem with waitForElement btw) It works relatively well for the needs of facebook.com and their many thousand tests. I don't have a better answer without getting too philosophical about the nature of tests and mocks, but like before, I'm happy we now have an actual primitive to batch these things.

Some cases, like Promises that cause state updates, aren't currently wrappable with synchronous act() and still cause a warning - do these cases actually cause bugs if you are using something like waitForElement?

The async version should capture this warning (unless you're willing to reproduce an environment described in react-act-examples, in which case you can use jest's fake timers to 'advance' synchronously). Like I mentioned above, it'll guarantee that the ui is relatively 'stable' at that point.

Is there anything that can be done to make this less obtrusive, either on the the react-testing-library side or through lint rules, async act(), etc.? Prior to this API, there were no React-specific details required in react-testing-library other than render.

There still mostly shouldn't, especially after the async version lands. It would be interesting if there were wrappers for jest's runAllTimers/advanceTimersByTime calls as well, but not many people use it in oss, so that maybe overkill.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment