You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Sinon/Enzyme测试React函数组件时Mock自定义Hook失败怎么办

问题原因

你写的sinon stub不生效的核心原因:useCustomHook是通过ES模块import语法导入的独立函数,并不是TestComponent组件的实例/静态属性,sinon.stub(TestComponent, 'useCustomHook')根本找不到可替换的目标挂载点,自然无法注入你预设的模拟返回值。

解决方案

提供两种无需修改业务组件代码的实现方式:

方案1:配合babel-plugin-rewire直接mock导入的Hook

这个方案可以直接替换组件文件内导入的useCustomHook实现,逻辑直观:

  • 先安装开发依赖babel-plugin-rewire,并在项目babel配置中启用该插件
  • 测试用例参考写法:
import TestComponent from './TestComponent';
import { mount } from 'enzyme';
import sinon from 'sinon';
import { expect } from 'chai';

describe('<TestComponent />', () => {
  afterEach(() => {
    // 用例执行完成后恢复原Hook实现,避免污染其他测试用例
    TestComponent.__ResetDependency__('useCustomHook');
  });

  it('renders an error when receiving one from hook', () => {
    // 注入mock的Hook返回值
    TestComponent.__Rewire__('useCustomHook', () => ({
      data: [],
      error: 'test error'
    }));

    const wrapper = mount(<TestComponent />);
    expect(wrapper.find('#test-component-error')).to.have.lengthOf(1);
    expect(wrapper.find('#test-component-error').text()).to.equal('test error');
  });

  it('renders select options when hook returns valid data', () => {
    const mockData = [{ name: 'option1' }, { name: 'option2' }];
    TestComponent.__Rewire__('useCustomHook', () => ({
      data: mockData,
      error: null
    }));

    const wrapper = mount(<TestComponent />);
    expect(wrapper.find('select option')).to.have.lengthOf(2);
  });
});

方案2:不mock Hook,直接mock Hook依赖的Service请求

如果不想引入额外的babel插件,可以直接stub自定义Hook内部调用的Service类方法,通过控制接口返回结果覆盖所有渲染分支,仅用sinon就能实现:

import TestComponent from './TestComponent';
import Service from './Service';
import { mount } from 'enzyme';
import sinon from 'sinon';
import { expect } from 'chai';

describe('<TestComponent />', () => {
  let getDataStub;
  beforeEach(() => {
    // 每个用例执行前替换Service的getData方法
    getDataStub = sinon.stub(Service.prototype, 'getData');
  });

  afterEach(() => {
    // 用例执行完恢复原方法
    getDataStub.restore();
  });

  it('renders an error when api request fails', async () => {
    // 模拟接口报错
    getDataStub.rejects('test error');
    const wrapper = mount(<TestComponent />);
    // 等待异步请求执行完成、组件状态更新
    await Promise.resolve();
    wrapper.update();

    expect(wrapper.find('#test-component-error')).to.have.lengthOf(1);
  });

  it('renders select options when api request succeeds', async () => {
    const mockData = [{ name: 'option1' }, { name: 'option2' }];
    // 模拟接口返回正常数据
    getDataStub.resolves(mockData);
    const wrapper = mount(<TestComponent />);
    await Promise.resolve();
    wrapper.update();

    expect(wrapper.find('select option')).to.have.lengthOf(2);
  });
});

注意:该方案中Hook内部是异步请求数据,mount组件后需要等待微任务队列执行完成,再调用wrapper.update()触发enzyme的视图重渲染,否则拿到的是组件初始渲染的空状态,断言会失败。

内容的提问来源于stack exchange,提问作者user19258133

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 05:27:31