React+TypeScript中Axios模拟测试失败,组件无法渲染数据
React+TypeScript模拟Axios测试Products组件数据渲染失败问题解决
问题描述
在React+TypeScript项目中测试Products组件的数据渲染时,组件始终处于loading状态,无法渲染出数据。已为fetchData端点编写测试用例fetchData.test.ts,并尝试在Products.test.tsx中mock该端点,但测试始终失败,组件状态始终为空数组。
原测试代码及报错
Products.test.tsx(原测试代码)
import '@testing-library/jest-dom'; import '@testing-library/jest-dom/extend-expect'; import React from 'react'; import Products from '../components/Products'; import { cleanup, render, fireEvent, waitFor, screen } from '@testing-library/react'; import {fetchData} from '../endpoints/fetchData' import axios from 'axios'; import { data } from '../temp/data'; jest.mock('../endpoints/fetchData'); it('should show render data', async () => { const mockReults = data; await (fetchData as jest.Mock).mockResolvedValue({ mockReults }) const { getByLabelText, getByText } = render(<Products />); await waitFor(() => expect(getByText('5-Minute Stories: In the Wild')).toBeInTheDocument() ) });
测试报错信息
● should show render data Unable to find an element with the text: 5-Minute Stories: In the Wild. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible. Ignored nodes: comments, script, style <body> <div> <div> loading... </div> </div> </body>
相关代码参考
fetchData.test.ts
import axios from 'axios'; import {fetchData} from '../endpoints/fetchData' import { data } from '../temp/data'; jest.mock('axios'); it('should show render data', async () => { const setProducts = jest.fn() const mockResponse = { data } axios.get = jest.fn().mockResolvedValue(mockResponse) const productsData = await fetchData(setProducts) expect(mockResponse.data).toEqual(productsData) });
Products组件核心代码
import React, { useState, useEffect } from 'react'; // ... 其他导入 function App() { const [products, setProducts] = useState<Products[]>([]); // ... 其他状态 useEffect(() => { fetchData(setProducts); setSearchValue(''); }, []); if(products.length === 0) { return <div>loading...</div> } // ... 其他逻辑 return ( <header className="App-header"> <div className="container">{mapProducts()}</div> </header> ); } export default App;
fetchData端点代码
export const fetchData = async (setter: (arg0: any) => any) => { try { const res = await axios.get('http://localhost:3030/products'); setter(res?.data); } catch (error) { console.log(error); } };
模拟数据示例
export const data = [ { id: 0, price: '9.99', productType: 'single-card', runtime: 1800, title: '5-Minute Stories: In the Wild', // ... 其他字段 }, ];
问题原因分析
- fetchData的mock方式错误:
fetchData的核心逻辑是接收setter函数并调用它传入接口数据,而非返回数据。原测试中使用mockResolvedValue返回数据,但组件依赖的是fetchData调用setProducts来更新状态,因此mock未生效,products状态始终为空,组件显示loading。 - 测试文本匹配错误:组件中标题的实际渲染文本是
Name: 5-Minute Stories: In the Wild,但测试中直接查找单独的标题文本,导致无法匹配到元素。
修复方案
修改Products.test.tsx,正确mockfetchData的行为,并修正文本匹配逻辑:
import '@testing-library/jest-dom'; import React from 'react'; import Products from '../components/Products'; import { render, waitFor, screen } from '@testing-library/react'; import { fetchData } from '../endpoints/fetchData'; import { data } from '../temp/data'; jest.mock('../endpoints/fetchData'); it('should render product data', async () => { // 模拟fetchData调用传入的setter函数,传入mock数据 (fetchData as jest.Mock).mockImplementation(async (setter) => { setter(data); }); render(<Products />); // 等待组件渲染完成,匹配包含目标标题的文本 await waitFor(() => { expect(screen.getByText(/5-Minute Stories: In the Wild/)).toBeInTheDocument(); // 或者匹配完整文本: // expect(screen.getByText('Name: 5-Minute Stories: In the Wild')).toBeInTheDocument(); }); });
修复说明
- mock逻辑修正:使用
mockImplementation模拟fetchData的实际行为,调用传入的setter函数并传入mock数据,确保组件的products状态被正确更新。 - 文本匹配修正:使用正则表达式匹配标题文本,避免因前缀(
Name:)导致的匹配失败;也可以直接匹配完整的渲染文本。
内容的提问来源于stack exchange,提问作者rednate95
相关产品推荐
相关产品推荐

