Jest+Enzyme测试React组件Mock API报错,提示fetchTheComments为空
报错根源与排查修正
核心报错原因
- 函数组件实例访问逻辑错误:App是无状态函数组件,Enzyme挂载这类组件时
wrapper.instance()会返回null,不存在class组件的this实例指向;且组件内部定义的fetchTheComments是函数闭包内的局部变量,不会对外暴露。测试代码中通过wrapper.instance().fetchTheComments()访问的方式,最终会拿到null值,这就是控制台提示“fetchTheComments函数为空”的直接原因。 - 业务代码存在React渲染反模式:
<p>标签内直接在渲染阶段调用异步函数fetchTheComments(),每次组件重渲染都会重复触发API请求,且异步函数返回的是Promise对象,JSON.stringify(Promise)只能得到空对象{},根本无法拿到实际接口返回内容,还会触发不受控的重复网络请求,干扰测试执行。 - Mock逻辑完全未生效:测试代码中自定义的
mockAxiosApi没有和真实的axios模块做绑定,缺少jest.mock("axios")声明,Jest根本不会将组件内导入的真实axios替换为自定义mock对象,等于mock逻辑和实际运行的代码完全脱节,自然无法拦截API请求。 - 测试用例逻辑顺序错误:当前测试流程是先模拟按钮点击触发请求,再给axios绑定mock实现,触发请求时mock还未挂载,无法拦截请求;且用例中查找的
comments节点在组件中根本不存在,没有对应的DOM元素可以匹配;所有异步操作完成前就直接做断言,拿到的都是初始渲染的空状态。
修正方案
业务代码修正
首先要消除渲染阶段的异步调用反模式,用状态管理存接口返回值,代码示例:
import React, { useState } from "react"; import "./App.css"; import axios from "axios"; function App() { // 新增状态存储接口返回的评论数据 const [comments, setComments] = useState(null); const fetchTheComments = async () => { try { const commentsFetched = await axios.get( `https://jsonplaceholder.typicode.com/comments/1` ); // 请求成功后更新状态,触发组件重渲染 setComments(commentsFetched.data); } catch (e) { console.error("请求出错", e); } }; return ( <div className="App"> <h1>Testing Jest-Enzyme</h1> <button id="fetch-comments" onClick={fetchTheComments} > Fetch </button> {/* 给p标签加专属id方便测试断言 */} <p id="comments-content"> {comments ? JSON.stringify(comments) : ""} </p> </div> ); } export default App;
测试代码修正
- 补充Jest的axios模块mock声明,替换真实网络请求
- 去掉对组件内部私有函数的直接访问,通过模拟用户交互触发业务逻辑
- 调整mock绑定的顺序,在模拟点击前完成mock实现的配置
- 等待异步操作执行完成后再做DOM断言
修正后的测试示例:
import App from "./App"; import { mount } from "enzyme"; // 导入真实axios,通过Jest声明将其替换为自动生成的mock import axios from "axios"; jest.mock("axios"); // 工具方法:等待所有异步微任务执行完成 const waitForAsync = () => new Promise(resolve => setTimeout(resolve, 0)); describe("App组件测试", () => { let wrapper; beforeEach(() => { // 每个用例前重置mock状态,避免用例间的逻辑污染 jest.clearAllMocks(); wrapper = mount(<App />); }); test("渲染正确的标题", () => { expect(wrapper.find("h1").text()).toBe("Testing Jest-Enzyme"); }); test("点击按钮成功请求并渲染评论内容", async () => { // 先绑定mock实现,再触发用户点击 axios.get.mockResolvedValue({ data: { id: 1, body: "测试评论内容" } }); // 模拟用户点击按钮的交互 wrapper.find("#fetch-comments").simulate("click"); // 等待异步请求、状态更新、组件重渲染全流程完成 await waitForAsync(); wrapper.update(); // 断言p标签的实际渲染内容 expect(wrapper.find("#comments-content").text()).toBe( JSON.stringify({ id: 1, body: "测试评论内容" }) ); // 断言接口请求参数符合预期 expect(axios.get).toHaveBeenCalledWith( "https://jsonplaceholder.typicode.com/comments/1" ); }); });
内容的提问来源于stack exchange,提问作者Murugesh Aravind
相关产品推荐
相关产品推荐

