Vue3 Composition API组件函数与Vuex Action的Spy/Stub测试问题
问题原因及解决办法
核心原因
<script setup>的模块私有特性:Vue3的<script setup>语法中,定义的函数(如b())默认是模块内部私有变量,不会挂载到组件实例(this)上。所以运行时调用this.b()会因为this上不存在该函数而报错。- 测试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
相关产品推荐
相关产品推荐

