使用Vitest+Vue Test Utils时,window.close模拟间谍未被调用问题
问题描述
我用Vitest结合Vue Test Utils做Vue组件方法的单元测试,其中一个测试点是:点击按钮时会调用组件的close()方法,该方法内部会调用浏览器的window.close():
const close = () => { console.log('close() called') window.close() }
测试时我尝试模拟window.close(),但测试失败,提示:expected "spy" to be called once, but got 0 times。不过我能在测试日志里看到console.log的输出,说明组件的close()方法确实被调用了。我的测试代码如下:
import { test, vi, expect } from "vitest"; import { mount } from "@vue/test-utils"; test('window.close is called when button is clicked', async () => { const wrapper = mount(MyComponent, { global: { plugins: [router] } }) const btn = wrapper.find('#exit-button') await btn.trigger('click') window.close = vi.fn() expect(window.close).toHaveBeenCalledOnce() })
解决方法
问题核心是模拟window.close()的时机错误:你是在点击按钮触发方法之后才把window.close替换成spy函数,这时候真实的window.close()已经执行完毕,spy根本没机会被调用。
正确的操作顺序是在组件挂载前就创建好spy并替换掉window.close,具体步骤如下:
- 先创建spy并覆盖
window.close
你可以直接赋值:
window.close = vi.fn()
或者用Vitest的spyOn方法(方便后续恢复):
vi.spyOn(window, 'close').mockImplementation(() => {})
- 再挂载组件、触发按钮点击
- 最后断言spy被调用
修改后的完整测试代码:
import { test, vi, expect } from "vitest"; import { mount } from "@vue/test-utils"; test('window.close is called when button is clicked', async () => { // 提前模拟window.close window.close = vi.fn() const wrapper = mount(MyComponent, { global: { plugins: [router] } }) const btn = wrapper.find('#exit-button') await btn.trigger('click') expect(window.close).toHaveBeenCalledOnce() })
如果需要在测试结束后恢复window.close的原始实现,避免影响其他测试,可以加上清理步骤:
test('window.close is called when button is clicked', async () => { // 保存原始方法 const originalClose = window.close // 模拟 window.close = vi.fn() const wrapper = mount(MyComponent, { global: { plugins: [router] } }) const btn = wrapper.find('#exit-button') await btn.trigger('click') expect(window.close).toHaveBeenCalledOnce() // 恢复原始方法 window.close = originalClose })
或者用Vitest的钩子自动清理所有mock:
test('window.close is called when button is clicked', async () => { vi.spyOn(window, 'close').mockImplementation(() => {}) const wrapper = mount(MyComponent, { global: { plugins: [router] } }) const btn = wrapper.find('#exit-button') await btn.trigger('click') expect(window.close).toHaveBeenCalledOnce() }) // 每个测试结束后恢复所有mock afterEach(() => { vi.restoreAllMocks() })
内容的提问来源于stack exchange,提问作者demiglace
相关产品推荐
相关产品推荐

