DEBUG.WIKI
丽萨主机;为跨境业务选好 IP;原生 / 住宅 IP · 按套餐选择;查看 IP →
晚安云;香港 CN2 ¥15/月
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS

Vitest 后一个 beforeAll 抛错,前一个返回的清理没有执行

afterAll 运行不代表 beforeAll 返回的回调也执行。5.0.4 可能在收集 hook 结果时丢失已完成的清理,5.0.5 已修正。

一个 beforeAll 已经打开资源并返回清理函数,后一个 beforeAll 抛错;最终 afterAll 日志出现了,前一个返回的清理却没有运行。若使用 Vitest 5.0.4,应检查这条 hook 结果收集路径,而不是只看测试断言有没有执行。

受影响结构可以缩成:

import { afterAll, beforeAll, test } from 'vitest';

beforeAll(() => () => console.log('returned cleanup'));
beforeAll(() => { throw new Error('setup failed'); });
afterAll(() => console.log('afterAll'));
test('case', () => {});

旧版本在所有同类 hook 成功后才把收集的回调交回外层。后一个 hook 失败,外层保存的回调列表仍是空的,finally 虽运行 afterAll,也拿不到先前那份清理。beforeEach 返回回调也有相同风险。

5.0.5 保留已经完成的清理

Vitest 5.0.5 的正式 runner 将清理列表交给 hook 调用过程逐项填充,平行 hook 也保留已成功返回的结果。正式发行记录包含“后续 hook 失败时仍运行清理”的修正。

项目兼容时使用包含修正的正式版,保存锁文件并确认真正运行的 Vitest。--sequence.hooks=parallel 不是旧版本的绕行:并行收集也可能因其中一个失败而丢掉整批结果。

不能更新时,显式安排清理入口

暂时采用旧版时,可以将关键资源清理放入明确的 afterAll,并让清理在资源尚未创建时也能安全调用。下面的 openResource 代表项目已有的资源创建函数,代码是其组织方式示例:

let resource;

beforeAll(async () => {
  resource = await openResource();
});

afterAll(async () => {
  const current = resource;
  resource = undefined;
  if (current) await current.close();
});

同一个资源不应同时保留原返回清理和这份 afterAll,造成双重关闭。迁移前核对清理顺序:返回的 cleanup 与显式 afterAll 的执行位置可能不同,其他 hook 若仍依赖资源,应按实际依赖关系排列。

资源创建函数自己在中途抛错、尚未返回句柄的情况,仍需由创建函数负责释放已取得的部分资源。这与“前一个 hook 已完整成功,后一个失败”是两条分支。测试进程被强制结束时,也不能承诺框架钩子一定执行。

验收应让第二个 beforeAll 有意失败,观察第一个资源只关闭一次,测试仍正确报告 setup 失败;再覆盖正常路径和 beforeEach 对应路径。不能为了得到成功退出码吞掉原错误,也不能把一条 console 日志当作连接、文件句柄或临时服务确已释放。

回退时恢复工具与清理组织方式的配套改动,尤其检查有没有留下两份清理。已经泄漏的外部资源需按测试环境的正常管理方式处理,更新依赖不会自动关闭上一次进程遗留的资源。