我正在尝试为一个 React 应用程序编写单元测试,该应用程序调用多个静态 API 端点来填充一堆不同的下拉元素选项。
下面是我的反应组件的精简版本。我做了一个定制的钩子
useFetchReferenceData
负责调用所有参考 API 并设置下拉选项。
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;
在 useFetchReferenceData 中,我有大约 14 个以下 fetch 调用和下拉构建器函数,如下所示。这些函数中的每一个都在自定义挂钩文件底部的异步 useEffect 中调用
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);
}, []);
在我的测试中,我尝试模拟所有这些 fetch 调用以覆盖 useFetchReferenceData 文件中的所有行。测试如下所示,我使用 jest-fetch-mock 库来模拟一系列 API 调用。
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>
);
});
});
上述测试通过,但我收到一堆以下警告,我想知道如何修复。
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 */
您的测试似乎没有测试任何内容 - 它调用
render
但之后什么也不做。您是否在代码片段中省略了 expect
调用?
无论如何,警告告诉您
fetch
调用正在异步完成,这会触发重新渲染,并且测试代码在不知情的情况下不会批准 React 重新渲染。如果您通过模拟用户输入同步触发重新渲染,那么您可以将这些用户输入事件包装在 act
中(请参阅 此示例)。由于它们是异步发生的,所以这是行不通的。您有几种选择:
render
调用包装在 act
中。我没有尝试过这种方法。waitFor
或 findBy
查询之一来确保在测试继续之前所有后台操作已完成。这是我的正常做法。 it('validate the search information view', async () => {
render(
<UserContext.Provider value={{ loggedInUser }}>
<SearchSubmitterProfile />
</UserContext.Provider>
);
await screen.findByText('something that only displays once all fetches finish');
// Or
await waitFor(() => expect(someComplexCriteria).toBeTruthy());
// Continue with test assertions
});