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

Vue3 Composition API组件函数与Vuex Action的Spy/Stub测试问题

问题原因及解决办法

核心原因

  1. <script setup>的模块私有特性:Vue3的<script setup>语法中,定义的函数(如b())默认是模块内部私有变量,不会挂载到组件实例(this)上。所以运行时调用this.b()会因为this上不存在该函数而报错。
  2. 测试spy失效的本质:直接在a()里调用b()是模块内的直接函数引用,而非组件实例的方法调用。如果你的测试是针对组件实例(wrapper.vm)的方法做spy,自然无法捕获到模块内部的函数调用。改成this.b()时,测试里的spy可能恰好挂在了组件实例上,所以能检测到,但这不符合<script setup>的运行逻辑。

解决办法

方案1:聚焦最终行为,忽略内部函数(推荐)

如果b()只是a()的内部辅助逻辑,不需要单独测试,直接验证最终的Vuex Action触发即可——单元测试的核心是验证组件的对外行为,而非内部实现细节。

// 测试代码
import { mount } from '@vue/test-utils'
import TestView from './testView.vue'
import { createStore } from 'vuex'

const store = createStore({
  actions: {
    actionA: cy.spy().as('actionA')
  }
})

const wrapper = mount(TestView, { global: { plugins: [store] } })
// 触发点击或直接调用a()
wrapper.vm.a()

// 断言actionA被调用
cy.get('@actionA').should('have.been.called')

方案2:导出内部函数,单独测试

如果必须验证b()的调用逻辑,将b()导出为模块的对外成员,这样测试时可以直接spy这个导出函数:

<!-- testView.vue -->
<script setup>
import { useStore } from 'vuex'

// 导出b函数,供测试使用
export const b = () => {
  const store = useStore()
  store.dispatch('actionA')
}

const a = () => {
  // 保持原有的直接调用方式,运行时正常
  b()
}

// 如果需要在测试中触发a,暴露到组件实例
defineExpose({ a })
</script>
// 测试代码
import { mount } from '@vue/test-utils'
import TestView, { b } from './testView.vue'
import { createStore } from 'vuex'

// spy导出的b函数
const spyB = cy.spy(b).as('spyB')
const store = createStore({
  actions: {
    actionA: cy.spy().as('actionA')
  }
})

const wrapper = mount(TestView, { global: { plugins: [store] } })
wrapper.vm.a()

// 断言b和actionA都被调用
cy.get('@spyB').should('have.been.called')
cy.get('@actionA').should('have.been.calledWith', 'actionA')

方案3:将函数挂载到组件实例(不推荐)

如果一定要通过组件实例调用,可以用defineExpose把b()暴露出去,同时在a()里通过组件实例调用,但这种方式违背<script setup>的设计意图,仅作应急参考:

<!-- testView.vue -->
<script setup>
import { useStore, getCurrentInstance } from 'vue'
const store = useStore()
const instance = getCurrentInstance()

const b = () => {
  store.dispatch('actionA')
}

const a = () => {
  // 通过组件实例调用b
  instance.proxy.b()
}

// 暴露b到组件实例
defineExpose({ b, a })
</script>

测试时可以spywrapper.vm.b(),运行时也不会报错,但这种写法冗余且不符合Composition API的风格,不建议长期使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 00:40:12