react: Provide a way to trigger useEffect from tests
Hello,
I tried testing components that use the cool new hooks API, but useEffect doesn’t seem to work with the test renderer.
Here’s a small failling Jest test:
import React, { useEffect } from "react";
import { create as render } from "react-test-renderer";
it("calls effect", () => {
return new Promise(resolve => {
render(<EffectfulComponent effect={resolve} />);
});
});
function EffectfulComponent({ effect }) {
useEffect(effect);
return null;
}
And here’s a minimal reproducing repo: https://github.com/skidding/react-test-useeffect
Note that other use APIs seemed to work (eg.
useContext).
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 28
- Comments: 23 (8 by maintainers)
Commits related to this issue
- Updated test Gave up on testing useEffect: https://github.com/facebook/react/issues/14050 — committed to Satyam/lacorazon by Satyam 6 years ago
We should have something to trigger them.
I think you don’t need such a hassle for this. Jest would automock if you create a file
<root>/__mocks__/react.jsand in there you can just…This is a great workaround as you don’t need to touch any code when this is somehow fixed, you will just remove the mock.
I wrote a longish doc on how to use .act() to flush effects and batch user and api interactions with React. https://github.com/threepointone/react-act-examples
You can manually run
tree.update()and effect hooks will be ran. Example:TIP: until it will be fixed in library I fixed my tests by mocking useEffect to return useLayoutEffect just in tests.
I have own useEffect module where is just
then in my component I use
and then in the component test I mock it like following
No. it has to be at least within the next ‘tick’, ie a macrotask on the js task queue, as soon as
act()exits (Or within the act() callback scope itself). I hope my explainer doc makes this clearer next week.(Bonus: it will also recursively flush effects/updates until the queue is empty, so you don’t miss any hanging effects/updates)
It’s triggered for initial render after the browser is idle.
The only reason next updates trigger it is because we flush passive effects before committing the next render. This is important to avoid inconsistencies.
So as a result for every Nth render, you’ll see N-1th passive effects flushed.
We’ll likely offer a way to flush them on demand too.
@malbernaz I would NOT recommend wrapping every setState like you did. The Act warning is useful for surfacing bugs, and your hack is equivalent to silencing it.
@threepointone I am trying to understand how async act fixes it. Here is an example of component I want to test. It should listen to size changes of the screen:
To test it, I mock the Dimensions object (in React Native):
Now I am trying to test the following:
This is how I will emulate the resize:
While trying, I encountered so many weird errors that I don’t understand…
In the end, this is how I got it to work:
Is this how I am supposed to do it?