Vitest:组件测试中如何Mock组合式函数?
Vue组件测试:Mock组合式函数并验证方法调用
问题背景
需要验证Vue组件点击按钮时,是否调用了组合式函数useForm返回的submit方法。最初使用vi.mock的测试代码无法生效,wrapper.vm找不到submit,mock也未生效;改用vi.doMock后实现了需求,但需要明确正确的mock方式及原测试失败的原因。
相关代码
组合式函数useForm
export default function useForm() { const form = ref({}) const submit = () => { console.log('submit this amazing form') } return { form, submit, } }
组件代码
<template> <form> <input v-model="form.name" type="text" /> <button class="submit-button" @click="submit">Submit</button> </form> </template> <script setup> import useForm from '@/composable/useForm' const { form, submit } = useForm() </script>
原测试失败原因分析
vi.mock写法错误:useForm是默认导出的函数,原代码直接展开importOriginal()拿到的函数并替换submit,没有正确匹配默认导出的结构,导致组件导入的还是原函数,mock未生效。- Spy创建时机错误:原测试先触发按钮点击,再创建spy,此时点击事件已经执行,spy无法捕捉到之前的调用。
<script setup>变量未暴露:默认情况下,<script setup>中声明的变量不会暴露到wrapper.vm,所以无法直接通过wrapper.vm.submit访问。
正确解决方案
方案1:正确使用vi.mock(推荐)
直接mock组合式函数的默认导出,返回包含mocksubmit的对象,无需修改组件代码:
import { mount } from '@vue/test-utils' import Component from '@/component/Component.vue' import useForm from '@/composable/useForm' import { ref } from 'vue' // 正确mock默认导出的组合式函数 vi.mock('@/composable/useForm', () => { const mockSubmit = vi.fn() return { default: () => ({ form: ref({}), // 保持原结构,也可mock为空对象 submit: mockSubmit }) } }) describe('Component', () => { let wrapper // 获取mock的submit函数 const mockSubmit = useForm().submit beforeEach(() => { wrapper = mount(Component) // 重置mock调用记录,避免测试间污染 mockSubmit.mockClear() }) it('按钮点击时调用submit方法', async () => { await wrapper.find('button.submit-button').trigger('click') // 直接断言mock函数的调用 expect(mockSubmit).toHaveBeenCalled() }) })
方案2:使用vi.doMock动态mock
vi.doMock会在当前测试用例中动态mock模块,需注意保持mock结构与原函数一致:
import { mount } from '@vue/test-utils' import { ref } from 'vue' describe('BaseForm', () => { it('提交表单时调用submit', async () => { const mockSubmit = vi.fn() // 正确mock默认导出的函数 vi.doMock('../../composables/useForm.ts', () => ({ default: () => ({ form: ref({}), submit: mockSubmit }) })) // 动态导入组件,确保mock生效 const { default: BaseForm } = await import('../BaseForm.vue') const wrapper = mount(BaseForm) await wrapper.find('button').trigger('click') expect(mockSubmit).toHaveBeenCalled() }) })
补充:若需从wrapper.vm访问submit
如果一定要通过wrapper.vm访问,需在组件中用defineExpose暴露变量,但不推荐为了测试修改组件逻辑:
<script setup> import useForm from '@/composable/useForm' const { form, submit } = useForm() // 暴露submit到wrapper.vm defineExpose({ submit }) </script>
内容的提问来源于stack exchange,提问作者Riza Khan
相关产品推荐
相关产品推荐

