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

vue/test-utils v2中如何mock widgetRef?解决不可扩展对象错误

Vue Test Utils v2 中 Mock $refs 子组件的解决方案

问题背景

之前使用 Vue Test Utils v1 时,通过直接修改 wrapper.vm.$refs.widgetRef 来 Mock 子组件方法:

const wrapper = factory();
wrapper.vm.$refs.widgetRef = {
  actionStep: vi.fn(),
  redraw: vi.fn(),
};

升级到 v2 后抛出错误:TypeError: Cannot add property widgetRef, object is not extensible,原因是 v2 中 $refs 对象被设置为不可扩展,无法直接新增属性。

解决方案

方法一:挂载时通过 stubs 替换子组件并注入 Mock 方法

这是最符合测试规范的方式,在挂载组件阶段直接 Stub 目标子组件,提前定义好 Mock 方法:

import { mount } from '@vue/test-utils';
import WidgetMaximized from './Component.vue';

// 定义 Mock 子组件
const mockChildComponent = {
  render() {
    return ''; // 空渲染避免干扰测试逻辑
  },
  methods: {
    actionStep: vi.fn(),
    redraw: vi.fn(),
  },
};

test('通过 stubs Mock widgetRef', () => {
  const wrapper = mount(WidgetMaximized, {
    stubs: {
      ChildComponent: mockChildComponent,
    },
  });

  // 触发父组件中调用子组件方法的逻辑
  wrapper.vm.someMethodThatCallsWidgetRef();

  // 验证 Mock 方法的调用情况
  expect(mockChildComponent.methods.actionStep).toHaveBeenCalled();
});

方法二:获取子组件实例后直接替换方法

如果子组件已正常渲染,可通过 findComponent 定位实例并替换其方法:

import { mount } from '@vue/test-utils';
import WidgetMaximized from './Component.vue';

test('直接替换子组件实例的方法', () => {
  const wrapper = mount(WidgetMaximized);
  
  // 通过 ref 定位子组件实例
  const childComponent = wrapper.findComponent({ ref: 'widgetRef' });
  
  // 将子组件方法替换为 Mock 函数
  childComponent.vm.actionStep = vi.fn();
  childComponent.vm.redraw = vi.fn();

  // 触发父组件相关逻辑
  wrapper.vm.someMethodThatCallsWidgetRef();

  // 验证方法调用
  expect(childComponent.vm.actionStep).toHaveBeenCalled();
});

方法三:修改 $refs 的可扩展性(不推荐)

如果必须直接操作 $refs,可通过 Object.defineProperty 强制添加属性(不推荐,可能违反测试规范):

import { mount } from '@vue/test-utils';
import WidgetMaximized from './Component.vue';

test('强制修改 $refs 添加 Mock 组件', () => {
  const wrapper = mount(WidgetMaximized);
  
  // 给 $refs 添加 widgetRef 属性
  Object.defineProperty(wrapper.vm.$refs, 'widgetRef', {
    value: {
      actionStep: vi.fn(),
      redraw: vi.fn(),
    },
    writable: true,
    configurable: true,
  });

  // 调用 Mock 方法并验证
  wrapper.vm.$refs.widgetRef.actionStep();
  expect(wrapper.vm.$refs.widgetRef.actionStep).toHaveBeenCalled();
});

补充说明

组件中使用的 vue-facing-decorator @Ref() 装饰器标记的 readonly 仅在 TypeScript 编译阶段生效,不会影响测试环境中对属性的修改操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 19:28:14