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

使用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,具体步骤如下:

  1. 先创建spy并覆盖window.close
    你可以直接赋值:
window.close = vi.fn()

或者用Vitest的spyOn方法(方便后续恢复):

vi.spyOn(window, 'close').mockImplementation(() => {})
  1. 再挂载组件、触发按钮点击
  2. 最后断言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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 06:27:24