如何用 using 声明在 Vitest 测试块结束时自动还原 spy 与 mock
如何用 using 声明在 Vitest 测试块结束时自动还原 spy 与 mock【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest在 Vitest 中spy 和 mock 安装之后需要在测试结束前还原否则状态会在测试之间泄漏。常规做法是在套件层写afterEach(() vi.restoreAllMocks())或在每个测试内联onTestFinished(() spy.mockRestore())。如果你的运行环境支持 TC39 Explicit Resource ManagementERM提案可以换一种更紧凑的写法把 spy 用using声明而不是const块退出时 Vitest 会自动还原。这条能力自 Vitest 3.2.0 起可用适用于vi.spyOn、vi.fn和vi.doMock。环境前提using依赖 ERM文档给出的支持条件如下Vitest ≥ 3.2.0TypeScript ≥ 5.2target: es2022或更高disposablelib 默认包含Node.js ≥ 24 提供原生运行时支持Node 22 需加--harmony风格的标志。环境满足这些条件即可直接使用下文写法不满足时见文末的替代方案。用 using 自动还原 vi.spyOn 的 spy把vi.spyOn的返回值用using声明测试结束时 spy 会被自动mockRestore不需要再写afterEachimport { expect, it, vi } from vitest function debug(message: string) { console.log(message) } it(calls console.log, () { using spy vi.spyOn(console, log).mockImplementation(() {}) debug(message) expect(spy).toHaveBeenCalled() }) // console.log is restored here without an afterEachvi.fn创建的 mock 函数同样可以用using声明块退出时自动还原。用 using 自动取消 vi.doMock 的 mockvi.doMock返回一个 disposable。对它用using块退出时 Vitest 会自动对该模块调用vi.doUnmockimport { expect, it, vi } from vitest it(uses the mocked module, then the real one, async () { { using _mock vi.doMock(./users, () ({ loadUser: () ({ id: 1, name: Alice }), })) const { loadUser } await import(./users) expect(loadUser(alice).name).toBe(Alice) } // ./users is unmocked from here on })注意vi.doMock只影响调用之后的动态导入静态import因提升机制不会被 mock所以上例中先using _mock、再await import(./users)。只在测试块的一部分生效using是块级作用域所以可以把 spy 只装在测试的某一段这是afterEach和onTestFinished都覆盖不到的场景——两者都在测试结束之后才运行import { expect, it, vi } from vitest it(only mocks fetch for the auth call, async () { // real fetch here await preloadConfig() { using fetchSpy vi.spyOn(globalThis, fetch) .mockResolvedValue(new Response({ok:true})) await login(alice, secret) expect(fetchSpy).toHaveBeenCalledOnce() } // real fetch is back await reportSuccess() })如果只有少数几处调用需要还原也可以用这种局部写法避免为整个项目打开全局的restoreMocks: true配置。如何验证还原已生效文档示例给出的判断方式有两处块内的断言证明 mock 在生效例如expect(spy).toHaveBeenCalled()、expect(loadUser(alice).name).toBe(Alice)块之后的代码直接使用原始实现文档以注释标明console.log is restored here和./users is unmocked from here onvi.doMock的官方示例进一步用第二个it导入同一模块来确认其不再被 mock。按同样的思路你可以把「块之后对原函数的调用」写成断言来核对还原结果。环境不支持 ERM 时的替代方案如果你的 TypeScript 或 Node.js 版本还不支持using整测试清理最接近的等价写法是onTestFinished它在测试完成后运行无论测试通过还是失败都会执行如果只在测试末尾手动 restore断言失败时还原代码不会执行import { expect, it, onTestFinished, vi } from vitest it(calls console.log, () { const spy vi.spyOn(console, log).mockImplementation(() {}) onTestFinished(() spy.mockRestore()) debug(message) expect(spy).toHaveBeenCalled() })限制在于onTestFinished无法在测试中途拆掉 spy「测试块内部分段还原」这种模式只有 ERM 的using能做到。相关文档完整 recipeAuto-Cleanup withusingAPI 说明vi.spyOn/vi.doMock/vi.restoreAllMocks、onTestFinished全局替代方案restoreMocks配置功能来源Vitest 3.2 发布说明【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考