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
相关产品推荐
相关产品推荐

