如何在RTL测试中Mock多个Fetch调用并传入自定义数据
问题描述
组件中存在两个独立的Fetch调用:
const fetchPopularCuisines = async() => { const data = await fetch(URL2) const jsonData = await data.json() setPopularCuisines(jsonData?.data?.cards[1]) } const fetchSuggestions = async() =>{ const data = await fetch(URL1) const jsonData = await data.json() setSearchResults(jsonData?.data?.suggestions) }
使用React Testing Library(RTL)测试组件时,现有测试代码如下:
// POPULAR_CUISINES_DATA是调用fetchPopularCuisines()时API返回的数据 global.fetch = jest.fn(()=>{ return Promise.resolve({ json : () => Promise.resolve(POPULAR_CUISINES_DATA) }) }) // 测试上述第一个函数[fetchPopularCuisines()] test('popular cuisines should render after making an API call', async ()=>{ const searchBar = render( <Provider store={store}> <SearchBar /> </Provider> ) await waitFor(()=>{ const cuisinesList = searchBar.getAllByTestId('cuisines-list') expect(cuisinesList.length).toBe(12) } ) })
当前测试第一个Fetch调用运行正常,但测试第二个fetchSuggestions()时,需要传入自定义数据SEARCH_RESULTS_DATA而非POPULAR_CUISINES_DATA,不知如何实现。同时想了解:若存在n个不同的Fetch调用,该如何分别Mock并为每个调用传入自定义数据?
解决方案
针对不同URL返回不同Mock数据
核心思路是让jest.fn()根据传入的URL参数返回对应数据。给mock的fetch函数添加逻辑,判断调用时传入的URL,再返回对应的数据结构即可。
测试fetchSuggestions()的示例代码
// 定义两个测试数据 const POPULAR_CUISINES_DATA = { /* 对应业务数据 */ }; const SEARCH_RESULTS_DATA = { /* 对应业务数据 */ }; // 重置mock的fetch,避免测试间状态污染 beforeEach(() => { global.fetch.mockClear(); }); // 测试fetchPopularCuisines test('popular cuisines should render after making an API call', async ()=>{ global.fetch.mockImplementation((url) => { if (url === URL2) { return Promise.resolve({ json: () => Promise.resolve(POPULAR_CUISINES_DATA) }); } // 其他URL可返回默认结构或抛出错误,按需调整 return Promise.reject(new Error('Unknown URL')); }); render( <Provider store={store}> <SearchBar /> </Provider> ); await waitFor(()=>{ const cuisinesList = getAllByTestId('cuisines-list'); expect(cuisinesList.length).toBe(12); }); }); // 测试fetchSuggestions test('search suggestions should render after making an API call', async ()=>{ global.fetch.mockImplementation((url) => { if (url === URL1) { return Promise.resolve({ json: () => Promise.resolve(SEARCH_RESULTS_DATA) }); } return Promise.reject(new Error('Unknown URL')); }); render( <Provider store={store}> <SearchBar /> </Provider> ); // 根据实际渲染的suggestions元素编写断言 await waitFor(()=>{ const suggestions = getAllByTestId('search-suggestions'); expect(suggestions.length).toBe(5); // 示例断言,需按实际业务调整 }); });
处理n个不同Fetch调用的通用方案
当存在多个不同URL的Fetch调用时,可以维护一个URL-数据映射表,让mock的fetch函数根据映射表返回对应数据,代码更易维护:
// 定义所有测试数据的映射表 const API_MOCK_DATA = { [URL1]: SEARCH_RESULTS_DATA, [URL2]: POPULAR_CUISINES_DATA, [URL3]: ANOTHER_DATA, // 新增URL对应的数据 // ... 可扩展更多条目 }; // 通用mock fetch实现 global.fetch.mockImplementation((url) => { const mockData = API_MOCK_DATA[url]; if (mockData) { return Promise.resolve({ json: () => Promise.resolve(mockData) }); } // 对未定义的URL,可抛出错误或返回空数据,按需调整 return Promise.reject(new Error(`No mock data for URL: ${url}`)); });
后续新增Fetch调用时,只需在API_MOCK_DATA中添加对应的URL和数据即可,无需修改mock的核心逻辑。
注意事项
- 每次测试前必须用
mockClear()重置fetch的mock,避免不同测试间的状态污染。 - 如果组件会同时触发多个Fetch请求(比如挂载时并行调用两个接口),可在mock实现中同时处理多个URL的判断,返回对应数据。
内容的提问来源于stack exchange,提问作者JAY PRAKASH
相关产品推荐
相关产品推荐

