vitest测试异步函数需显式等待promise完成:正确写法为async/await或返回promise;断言异常用await expect(...).rejects;mock依赖用vi.mock()配合mockresolvedvalue;微任务延迟用vi.waitfor()。

用 Vitest 测试异步函数,关键在于让测试框架明确知道“要等什么”,而不是让 Promise 悄悄溜走。Vitest 默认支持 async/await,但写法稍有不慎就会导致断言没执行、错误被忽略、测试误通过。
必须用 async/await 或返回 Promise
测试函数本身得是异步的,或显式返回 Promise。Vitest 会自动等待它 settle。
- ✅ 正确:标记 async 并 await 被测函数
const user = await fetchUser(123);
expect(user.name).toBe('Alice');
});
- ✅ 正确:不写 async,但 return Promise(Vitest 会等)
return fetchUser(123).then(user => {
expect(user.name).toBe('Alice');
});
});
- ❌ 错误:只调用 .then() 却不 return,测试立即结束
fetchUser(123).then(user => expect(user.name).toBe('Alice')); // 断言可能根本没跑
});
测试抛出异常的异步函数
异步函数出错时,抛的是 rejected Promise,不能用 .toThrow(),得用 .rejects 链式断言,并且一定要 await。
- ✅ 正确:await + .rejects.toThrow()
await expect(fetchUser(-1)).rejects.toThrow('ID must be positive');
});
- ✅ 可校验错误结构:.rejects.toMatchObject()
name: 'ValidationError',
code: 'INVALID_ID'
});
- ❌ 错误:漏掉 await,断言对象是 pending Promise,结果不可靠
- ❌ 错误:用 .toThrow() 包裹 async 函数调用,语法不匹配
Mock 异步依赖(如 API 调用)
真实请求会让测试变慢、不稳定、依赖网络。用 vi.mock() 替换模块,再用 mockResolvedValue 或 mockRejectedValue 控制返回行为。
- 先 mock 掉 fetch 或封装的请求函数
const mod = await importOriginal();
return {
...mod,
fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Bob' })
};
});
- 测试中直接调用,无需改逻辑,断言结果即可
const user = await fetchUser(1);
expect(user.name).toBe('Bob');
expect(fetchUser).toHaveBeenCalledWith(1);
});
处理微任务和防抖等延迟更新
如果函数内部用了 Promise.resolve().then()、queueMicrotask 或防抖(debounce),状态更新不在同步完成,需主动等待。
- ✅ 用
vi.waitFor()等待条件成立(推荐)
form.setInput('new value');
expect(form.isDirty).toBe(false);
await vi.waitFor(() => expect(form.isDirty).toBe(true));
});
- ✅ 或用
await Promise.resolve()触发一次微任务轮询 - ⚠️ 不要用
vi.runOnlyPendingTimers()—— 它只处理 setTimeout 类宏任务,对微任务无效
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











