Vitest与Vue-Test-Utils测试组件内函数调用失败问题
Vue3组件方法点击触发测试失败解决方案
问题背景
在Vue3项目的<script setup>组件中,测试按钮点击是否触发内部example函数时,始终收到「函数未被调用」的报错,且覆盖率报告显示该函数未被测试。
组件代码(Example.vue)
<script setup lang="ts"> const example = () => console.log("TEST"); </script> <template> <div> <button data-testid="button-example" @click="example">Click</button> </div> </template>
测试代码(example.spec.ts)
import { describe, vi, it, beforeEach, expect } from "vitest"; import { mount, VueWrapper } from "@vue/test-utils"; import Example from "../src/components/Example.vue"; describe("example testing", () => { let wrapper: VueWrapper<Example & { [key: string]: any }>; beforeEach(() => { wrapper = mount(Example); }); it("component mounted", () => { expect(wrapper.exists()).toBeTruthy(); }); it("example function should be called", async () => { const example = vi.spyOn(wrapper.vm, "example"); await wrapper.find('[data-testid="button-example"]').trigger("click"); expect(example).toHaveBeenCalled(); }); });
报错信息
FAIL test/example.spec.ts > example testing > example function should be called AssertionError: expected "example" to be called at least once ❯ test/example.spec.ts:21:21 19| await wrapper.find('[data-testid="button-example"]').trigger("click"); 20| 21| expect(example).toHaveBeenCalled(); | ^ 22| }); 23| });
已尝试的无效方法
- 先触发点击再创建spy:
it("example function should be called", async () => { await wrapper.find('[data-testid="button-example"]').trigger("click"); const example = vi.spyOn(wrapper.vm, "example"); expect(example).toHaveBeenCalled(); });
报错:This function is not spy or call spy!
- 直接断言实例方法:
it("example function should be called", async () => { await wrapper.find('[data-testid="button-example"]').trigger("click"); expect(wrapper.vm.example).toHaveBeenCalled(); });
报错:This function is not spy or call spy!
解决方案
方案1:将方法暴露到组件实例
<script setup>中声明的方法默认是私有的,不会挂载到组件实例上,需要通过defineExpose主动导出:
修改Example.vue:
<script setup lang="ts"> const example = () => console.log("TEST"); defineExpose({ example }); // 新增此行,暴露方法到组件实例 </script> <template> <div> <button data-testid="button-example" @click="example">Click</button> </div> </template>
此时原测试代码即可正常运行,spyOn能够正确捕获到方法调用。
方案2:测试方法的副作用(无需暴露方法)
如果不想修改组件代码,可以测试方法执行后的副作用。比如本例中方法会打印日志,可通过监听console.log来验证:
修改测试用例:
it("example function should be called", async () => { // 监听console.log并mock实现 const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); await wrapper.find('[data-testid="button-example"]').trigger("click"); // 断言日志是否被正确打印 expect(logSpy).toHaveBeenCalledWith("TEST"); // 恢复console.log的原始实现 logSpy.mockRestore(); });
原因说明
<script setup>是Vue3的语法糖,其内部声明的变量、方法默认处于封闭作用域,不会自动暴露给组件实例(wrapper.vm)。因此直接通过wrapper.vm.example获取到的引用,和模板中绑定的example函数不是同一个,导致spy无法捕获到调用。
内容的提问来源于stack exchange,提问作者Mocha_
相关产品推荐
相关产品推荐

