如何在Vitest中模拟AbortSignal.timeout接口函数?
问题分析与解决方案
你遇到的核心问题是:AbortSignal.timeout 是浏览器/Node.js 全局 API,并非你编写的 fetch.ts 模块导出的成员,所以你之前针对模块的 vi.mock 操作完全无效;同时 Vitest 默认运行在 Node 环境中,Node 18 以下版本原生不支持 AbortSignal.timeout,导致直接调用或模拟时抛出「不是函数」的错误。
下面是具体的修正方案:
正确模拟全局 AbortSignal.timeout
方案一:直接覆盖全局对象
在测试文件的开头或 beforeEach 钩子中,直接为全局 AbortSignal 添加 timeout 方法并模拟:
// 测试文件 import { fetchWithTimeout } from '~~/src/config/fetch'; describe('fetch 超时逻辑', () => { beforeEach(() => { vi.clearAllMocks(); // 模拟全局 AbortSignal.timeout,返回符合 AbortSignal 接口的对象 global.AbortSignal = { ...global.AbortSignal, timeout: vi.fn(() => ({ aborted: false, onabort: null, addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn() })) as typeof AbortSignal.timeout }; }); it('应该调用 AbortSignal.timeout 并传入 8000', async () => { await fetchWithTimeout('https://example.com'); expect(global.AbortSignal.timeout).toHaveBeenCalledWith(8000); }); });
方案二:使用 vi.spyOn(适用于 Node 18+ 环境)
如果你的测试环境是 Node 18 或以上(原生支持 AbortSignal.timeout),可以直接通过 spyOn 替换实现:
// 测试文件 import { fetchWithTimeout } from '~~/src/config/fetch'; describe('fetch 超时逻辑', () => { beforeEach(() => { vi.spyOn(global.AbortSignal, 'timeout').mockReturnValue({ aborted: false, onabort: null, addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn() }); }); afterEach(() => { vi.restoreAllMocks(); }); it('应该正确设置超时信号', async () => { await fetchWithTimeout('https://example.com'); expect(AbortSignal.timeout).toHaveBeenCalledWith(8000); }); });
你之前的错误点总结
- 错误的 mock 目标:你尝试通过
vi.mock('~~/src/config/fetch')模拟AbortSignal,但AbortSignal是全局 API,并非该模块导出的内容,所以这种操作完全无效。 - 环境兼容性问题:Node 18 以下版本没有原生的
AbortSignal.timeout,直接调用vi.spyOn(AbortSignal, 'timeout')会因为方法不存在而报错,必须先在全局对象上定义该方法再模拟。
内容的提问来源于stack exchange,提问作者Ruben
相关产品推荐
相关产品推荐

