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

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>

原测试失败原因分析

  1. vi.mock写法错误:useForm是默认导出的函数,原代码直接展开importOriginal()拿到的函数并替换submit,没有正确匹配默认导出的结构,导致组件导入的还是原函数,mock未生效。
  2. Spy创建时机错误:原测试先触发按钮点击,再创建spy,此时点击事件已经执行,spy无法捕捉到之前的调用。
  3. <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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 00:10:36