I am trying to write unit tests for a react application that calls multiple static API endpoints to populate a bunch of different dropdown element options.
A stripped down version of my react component is below. I have made a custom hook
useFetchReferenceData that takes care of calling all the reference APIs and setting the dropdown options.
export function SearchSubmitterProfile() {
const [search, setSearch] = useState(true);
const [viewResults, setViewResults] = useState(false);
const [createSubmitterProfileState, setCreateSubmitterProfileState] = useState(INITIAL_STATE);
useFetchReferenceData(
createSubmitterProfileState,
setCreateSubmitterProfileState
);
return (
<div id="search-sub-profile" key="search-sub-profile" className={styles.grayBg}>
{search ? (
<ConnectedSearchView
/>
) : null}
{viewResults ? (
<ConnectedSubmitterProfile
/>
) : null}
</div>
);
}
export default SearchSubmitterProfile;
In useFetchReferenceData, I have about 14 of the below fetch calls and dropdown builder functions like the below. Each of these functions is called in an asynchronous useEffect at the bottom of the custom hook file
const { run: getLanguageIndicators } = useFetchye(`${subApiUrl}/submlanguageind`, {
credentials: 'include',
headers: getDefaultHeaders(),
defer: true,
});
const buildLanguagIndDropdown = async () => {
const currentState = { ...createSubmitterProfileState };
const { data } = await getLanguageIndicators();
if (data.status === 200) {
const result = [];
result.push(DROPDOWN_STARTER);
data.body.map((element) => result.push({
value: `${element.dialect_language_code}`,
label: `${element.dialect_language_code} - ${element.dialect_name}`,
}));
currentState.staticFieldState.submitterLanguageIndicator.options = result;
setCreateSubmitterProfileState(currentState);
} else {
errorList.push('Error retrieving language indicators');
}
};
useEffect(() => {
const fetchAllData = async () => {
await buildRegionsDropDown();
await buildLanguagIndDropdown();
... 14 more of these
};
fetchAllData().catch(console.error);
}, []);
In my tests I am trying to mock all of these fetch calls to cover all of the lines in the useFetchReferenceData file. The test looks like the below, and I am using the jest-fetch-mock library to mock the series of API calls.
describe('Search Submitter Profile reference data', () => {
beforeEach(() => {
enableFetchMocks();
fetch.mockResponses(
[
JSON.stringify(mockRegions),
{ status: 200 },
],
[
JSON.stringify(mockLanguageIndicators),
{ status: 200 },
],
... 14 more
);
});
it('validate the search information view', async () => {
render(
<UserContext.Provider value={{ loggedInUser }}>
<SearchSubmitterProfile />
</UserContext.Provider>
);
});
});
The above test passes, but I get a bunch of the following warnings that I am wondering how to fix.
console.error
Warning: An update to SearchSubmitterProfile 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 */
Your test doesn't appear to test anything - it calls
renderbut does nothing after that. Did you omit yourexpectcalls from your code snippet?Anyway, the warnings are telling you that the
fetchcalls are finishing asynchronously, which triggers re-renders, and the test code doesn't approve of React re-rendering without knowing about. If you were triggering re-renders synchronously, by simulating user input, then you could wrap those user input events inact(see this example). Since they're occurring asynchronously, that won't work. You have a couple of alternatives:rendercall inact. I have not tried this approach.waitForor one of thefindByqueries to make sure that any background operations have finished before your test continues. This is my normal approach.