Vue3+Vitest+Vue Test Utils检测组件函数调用:已defineExpose仍失效
解决Vue3 script setup组件Vitest测试中函数Spy检测失败的问题
问题分析
你遇到的问题由三个核心原因导致:
- 测试代码中组件名称拼写错误,导致挂载了错误的组件
- 测试环境未配置Vue I18n,组件初始化失败
- Spy创建时机晚于组件初始渲染时的函数调用,无法捕捉到初始调用
解决方案
1. 修正组件名称拼写错误
确保测试中导入和挂载的组件名称与实际组件文件名一致:
// 正确导入组件 import DocumentDiscardModal from './DocumentDiscardModal.vue'; // 挂载时使用正确的组件名 const wrapper = mount(DocumentDiscardModal, { // ...后续配置 });
2. 配置测试用Vue I18n实例
组件依赖vue-i18n,测试环境必须提供对应的实例才能正常初始化:
import { createI18n } from 'vue-i18n'; import { mount } from '@vue/test-utils'; import DocumentDiscardModal from './DocumentDiscardModal.vue'; // 创建测试用的国际化实例 const testI18n = createI18n({ locale: 'en', messages: { en: { select_an_option: 'Select an option', discard_document: 'Discard Document', discard_document_argument: 'Please select a reason for discarding.', wrong_document: 'Wrong Document', quality_issue: 'Quality Issue', other: 'Other', cancel: 'Cancel', discard: 'Discard' } } });
3. 捕捉初始渲染时的函数调用
要检测组件初始渲染时getOptions的调用,需要在挂载前就将函数替换为Spy:
describe('DocumentDiscardModal.vue', () => { let getOptionsSpy: vi.SpyInstance; // 提前mock组件,注入Spy vi.mock('./DocumentDiscardModal.vue', async () => { const originalComponent = await vi.importActual('./DocumentDiscardModal.vue'); // 创建Spy并预设返回值 getOptionsSpy = vi.fn(() => [ { id: '0', label: 'Select an option', disabled: true }, { id: '1', label: 'Wrong Document' }, { id: '2', label: 'Quality Issue' }, { id: '3', label: 'Other' } ]); return { ...originalComponent, // 重写setup函数,替换目标函数为Spy setup(props: any, ctx: any) { const originalSetup = originalComponent.setup as (props: any, ctx: any) => any; const setupResult = originalSetup(props, ctx); setupResult.getOptions = getOptionsSpy; return setupResult; } }; }); test('getOptions should be called on initial render', () => { // 挂载组件,初始渲染会调用Spy mount(DocumentDiscardModal, { global: { plugins: [testI18n] }, props: { id: 'document-discard-modal', isOpen: true, }, }); // 验证Spy被调用 expect(getOptionsSpy).toHaveBeenCalled(); }); });
替代方案(检测重新渲染时的调用)
如果不想使用vi.mock,可挂载组件后创建Spy并强制重新渲染:
describe('DocumentDiscardModal.vue', () => { test('getOptions should be called on re-render', () => { const wrapper = mount(DocumentDiscardModal, { global: { plugins: [testI18n] }, props: { id: 'document-discard-modal', isOpen: true, }, }); // 创建Spy const getOptionsSpy = vi.spyOn(wrapper.vm, 'getOptions'); // 强制组件重新渲染,触发函数调用 wrapper.vm.$forceUpdate(); // 验证Spy被调用 expect(getOptionsSpy).toHaveBeenCalled(); }); });
内容的提问来源于stack exchange,提问作者Ricardo de Vries
相关产品推荐
相关产品推荐

