react-testing-library: useEffect not triggering inside jest
react-testing-libraryversion: 5.2.3reactversion: 16.7.0-alpha.0nodeversion: CodeSandboxnpm(oryarn) version: CodeSandbox
Relevant code or config:
let a = false;
function Test() {
React.useEffect(() => {
a = true;
});
return <div>Hello World</div>;
}
render(<Test />);
expect(a).toBe(true);
What you did:
I’m trying to test functions with the useEffect hook inside jest.
What happened:
The useEffect hook is not executed correctly inside a jest environment after calling render(<Test />). However it appears to be working correctly if it is called directly in a browser environment.
Reproduction:
Working example: https://codesandbox.io/s/l947z8v6xq (index.js) Not working example in jest: https://codesandbox.io/s/7k5oj92740 (index.test.js)
Problem description:
The useEffect hook should have been executed after the render call
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 16
- Comments: 32 (16 by maintainers)
That’s because React doesn’t run the hook in sync mode. If you rerender the component the second time, it changes
atotrue: https://codesandbox.io/s/m5m5yqrr18For some reason, this workaround:
changes nothing to my Jest tests, effects just never happen. Only manual replacement of
useEffectwithuseLayoutEffectin components does the trick. I know it’s not a QA thread, but maybe other people are experiencing the same… so any help will be appreciated.Could it be that
showAfterDelayonly schedules after 500ms, so you need to wait a bit longer than that before running synchronousexpects? Try waiting with the mutation observer helperwaitForDomChangeor one of the other helpers.You may also be interesting in http://kcd.im/hooks-and-suspense
I have a video in there that explains one of the big nuances with testing effect hooks.
I do plan on creating a small function called
flushEffectsor something that’ll make this easier.Currently the way that I dislike the least for making this work nicely is to put this in my setup file:
I don’t like it, but it’s my favorite anyway…
It seems the topic diverged a little bit but going back to the original issue. Why not just use
jest.spyOn(React, 'useEffect')and test it this way?Noooooooo. That would be an implementation detail. The user don’t actually flush the effects. In fact, they don’t even know what an effect isssss 😕
@kentcdodds Have you considering this workaround approach? Feels much easier imo … https://github.com/facebook/react/issues/14050#issuecomment-438173736
@wzrdzl There is another simple workaround for your case — you just need to rerender it once again 😄
Let me explain how it works (probably I’m wrong):
I agree this workaround is ugly, but not sure if it makes sense to fix the case before React core team releases the stable version. Furthermore example with lifecycle alternative works well, so I think
jsdomhandles React event logic correctly.I’ve created a repo with the example so you can clone and make sure: https://github.com/sivkoff/hooks-testing-sandbox Unfortunatelly CodeSandbox doesn’t support mocking timers, so you need to clone it to localhost.
@FredyC I followed the documentation you have linked to, but no wrapping of
renderoreventin theactfunction allows for the effects to asynchronously update the component after its initial render.Would be great to work together on this issue to get something more obvious working for tests which need to assert side effects on the DOM. At the moment anything in
useEffectis untestable if it has an effect on the DOM.@alflennik https://github.com/threepointone/react-act-examples and please use latest RTL
The lack of coverage for my useEffect code led me here. It’s been three months since the issue was closed, but I’m feeling the pain right now. I would absolutely love an automatic flushEffects feature. Without it, how am I supposed to test my side effects? I am not going to introduce timeouts into my tests.
I’m actually starting to think that it’s better to manually trigger effects. That’s why I added the experiential
flushEffectsfunction (see the docs).