I have a component that makes use of the useEffect hook to make an asynchronous call an api (or in the test case a mock of a call to an api) and then call the setState hook with the result.
I've written tests that assert on the outcome of the effect (ie content being returned from the api). I'm using v1.14.0 of the adapter for react-16 my tests pass, snapshots are created and everything appears to be working well, however, a warning is repeatedly logged out from react-dom.
Here's the test in question:
import { mount } from 'enzyme';
import { flushPromises, renderHook, HOOK_WRAPPER_ID } from 'utils/tests';
import useData from './use-data';
const mockURL = 'https://example.com';
const mockConfig = {
data: ':dataHere',
};
describe('useData', () => {
it('fetches data config', async () => {
fetch.mockResponseOnce(JSON.stringify(mockConfig));
const wrapper = mount(renderHook(() => useData(mockURL)));
await flushPromises();
wrapper.update();
const dataConfig = wrapper.find(`#${HOOK_WRAPPER_ID}`).props().hook;
expect(dataConfig).toEqual(mockConfig);
});
});
Here's the warning:
PASS src/hooks/use-data.test.js
● Console
console.error node_modules/react-dom/cjs/react-dom.development.js:506
Warning: An update to TestHookWrapper inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act
in TestHookWrapper (created by WrapperComponent)
in WrapperComponent
https://github.com/airbnb/enzyme/issues/2073 claims to have fixed this, but even after upgrading both enzyme and enzyme-adapter-react-16 I still encounter this warning.
For no warning to be issued.
| library | version
| ------------------- | -------
| enzyme | v3.10.0
| react | v16.8.6
| react-dom | v16.8.6
| react-test-renderer | n/a
| adapter (below) | v1.14.0
enzyme now wraps the things it knows about in act, but if your test is updating it (like in flushPromises) you have to manually wrap that in act.
I am testing a custom hook above that uses useEffect and my custom hook is async... how do you wrap that in act? I can't simply await on act because it doesn't return anything...
I am trying this:
await act(async () => {
await flushPromises();
});
but I get this error:
console.error node_modules/react-dom/cjs/react-dom-test-utils.development.js:100
Warning: The callback passed to ReactTestUtils.act(...) function must not return anything.
It looks like you wrote ReactTestUtils.act(async () => ...), or returned a Promise from the callback passed to it. Putting asynchronous logic inside ReactTestUtils.act(...) is not supported.
console.error node_modules/react-dom/cjs/react-dom-test-utils.development.js:100
Warning: Do not await the result of calling ReactTestUtils.act(...), it is not a Promise.
console.error node_modules/react-dom/cjs/react-dom.development.js:506
Warning: An update to TestHookWrapper inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act
in TestHookWrapper (created by WrapperComponent)
in WrapperComponent
indeed, act is synchronous.
Can you share the code of renderHook?
This issue is actually due to async/await support not being present in the act testing util provided by react-dom/test-utils. You can see more about the issue here: https://github.com/facebook/react/issues/14769
The solution that worked for me was upgrading both react and react-dom to v16.9.0-alpha.0. Then I modified my test above to look like:
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { flushPromises, renderHook, HOOK_WRAPPER_ID } from 'utils/tests';
import useData from './use-data';
const mockURL = 'https://example.com';
const mockConfig = {
data: ':dataHere',
};
describe('useData', () => {
it('fetches data config', async () => {
fetch.mockResponseOnce(JSON.stringify(mockConfig));
const wrapper = mount(renderHook(() => useData(mockURL)));
// now act supports async/await syntax
await act(async () => {
await flushPromises();
});
wrapper.update();
const dataConfig = wrapper.find(`#${HOOK_WRAPPER_ID}`).props().hook;
expect(dataConfig).toEqual(mockConfig);
});
});
@ljharb Thanks for your help and patience troubleshooting this 😃
Most helpful comment
This issue is actually due to
async/awaitsupport not being present in theacttesting util provided byreact-dom/test-utils. You can see more about the issue here: https://github.com/facebook/react/issues/14769The solution that worked for me was upgrading both
reactandreact-domtov16.9.0-alpha.0. Then I modified my test above to look like:@ljharb Thanks for your help and patience troubleshooting this 😃