使用Jest与TypeScript进行React Canvas组件单元测试时mount()报错及函数调用验证问题
让我们一步步拆解你的问题,先弄清楚错误的根源,再给出可行的解决方案:
错误原因分析
1. mount时的Cannot read properties of undefined (reading 'child')错误
这个错误的核心是你在beforeEach中对props.draw的赋值完全错误:
props = { draw: jest.fn().getMockImplementation };
jest.fn().getMockImplementation是一个方法引用,不是一个可执行的mock函数。当Enzyme尝试渲染组件并调用这个"函数"时,内部会出现异常,导致渲染失败。正确的做法是直接用jest.fn()创建一个可以被追踪调用的mock函数。
2. mock useEffect的错误写法
你尝试直接调用useEffect.getMockImplementation()是完全错误的:useEffect是React的内置钩子,你需要通过jest.spyOn来mock它,而不是直接调用这个不存在的方法。不过实际上,在这个测试场景中我们根本不需要mock useEffect——我们要测试的是组件的正常行为:当组件挂载时,useEffect会自动执行并调用传入的draw函数。
完整修正后的测试代码
以下是可以正常运行的测试代码,同时能正确验证draw函数是否被调用:
import React from 'react' import { configure, shallow, mount } from "enzyme"; import Adapter from "enzyme-adapter-react-16"; import GraphicCanvas, { IGraphicCanvas } from "./GraphCanvas"; import { drawCircle } from "./DrawCircle" configure({ adapter: new Adapter() }); describe("GraphicCanvas", () => { let props: IGraphicCanvas; beforeEach(() => { // 1. 创建一个可追踪的mock函数作为draw属性 props = { draw: jest.fn() }; // 2. Mock canvas的getContext方法,避免测试环境中canvas API的兼容性问题 jest.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue({ fillStyle: '', beginPath: jest.fn(), arc: jest.fn(), fill: jest.fn() } as unknown as CanvasRenderingContext2D); }); // 重置所有mock,避免测试用例之间的污染 afterEach(() => { jest.restoreAllMocks(); }); it("renders without crashing", () => { shallow(<GraphicCanvas draw={drawCircle} />); }); it("calls the draw function passed in props when component mounts", () => { // 挂载组件 mount(<GraphicCanvas {...props} />); // 断言draw函数被调用过至少一次 expect(props.draw).toHaveBeenCalled(); // 可选:进一步断言draw函数被传入了Canvas上下文对象 expect(props.draw).toHaveBeenCalledWith(expect.any(CanvasRenderingContext2D)); }); });
关键修正点说明
正确创建mock函数:
用jest.fn()创建的mock函数可以被Jest追踪调用次数、调用参数等,完全满足你的测试需求,替代之前错误的getMockImplementation引用。Mock Canvas API:
在测试环境中,canvas.getContext('2d')可能返回null或者存在兼容性问题,我们通过jest.spyOnmock这个方法,返回一个模拟的Canvas上下文对象,确保组件内的useEffect能正常执行到props.draw(context)这一步。不需要mock useEffect:
我们的测试目标是验证组件的正常业务逻辑——当组件挂载时,是否会调用传入的draw函数。保持useEffect的原生行为,才能真实模拟组件的运行状态,测试结果更可靠。
额外提示
如果你想直接测试drawCircle函数是否被调用(而不是用mock函数),可以这样修改测试用例:
it("calls drawCircle when component mounts", () => { // Spy on drawCircle函数,追踪它的调用 const drawCircleSpy = jest.spyOn(require("./DrawCircle"), 'drawCircle'); mount(<GraphicCanvas draw={drawCircle} />); expect(drawCircleSpy).toHaveBeenCalled(); drawCircleSpy.mockRestore(); });
这样可以直接验证真实的drawCircle函数是否被组件调用。
内容的提问来源于stack exchange,提问作者Rory

