应使用await expect().rejects断言async函数拒绝,因async函数返回Promise,错误是异步reject而非同步throw;toThrow()仅适用于同步错误,对Promise无效。

直接用 expect().rejects 配合匹配器断言拒绝原因,这是最可靠、语义最清晰的方式。它自动等待 Promise 完成,提取 rejection reason,并防止未处理的拒绝警告干扰测试。
为什么不能用 toThrow()
async 函数本身是同步返回 Promise 的,expect(async () => { ... }).toThrow() 实际检查的是函数定义是否同步抛错——而它永远不会。这种写法永远通过或报类型错误,完全无法验证异步拒绝。
- toThrow() 只适用于同步抛出的错误
- 对 async 函数或 Promise 调用使用 toThrow() 属于逻辑误用
- Jest 会提示 “Expected the function to throw an error” 却不告诉你它根本没等异步执行
rejects 后接什么匹配器合适
.rejects 是一个断言修饰符,后面必须跟具体校验方式。根据错误类型选择:
- 错误消息文本匹配:
.rejects.toThrow('Network error')(推荐用于普通 Error) - 错误实例类型判断:
.rejects.toBeInstanceOf(ValidationError) - 自定义错误对象结构:
.rejects.toMatchObject({ code: 'INVALID_TOKEN', message: /token/i }) - 避免用
.toEqual(new Error('...')):Error 对象不满足浅相等,会失败
常见错误写法及修正
以下写法看似合理,实则隐患明显:
- ❌
await getUser().catch(err => expect(err).toMatch(...))—— 没有让测试框架感知拒绝,可能漏掉未捕获警告 - ❌
try { await getUser(); } catch (e) { expect(e).toBe(...) }—— 手动 try/catch 冗余,且若忘记 return 或 await,测试可能提前通过 - ✅ 正确:
await expect(getUser()).rejects.toThrow('Not Found')—— 一行解决等待 + 断言 + 错误捕获
测试真实 API 拒绝时的小技巧
如果被测函数内部调用 fetch 或 axios,记得在测试前 mock 掉网络请求,确保拒绝可预测:
- Jest 中用
jest.mock('node-fetch')或 mock axios 的get方法返回 rejected Promise - Vitest 中可用
vi.mock('axios', () => ({ default: { get: vi.fn(() => Promise.reject(new Error('timeout'))) } })) - 确保 mock 行为与实际拒绝路径一致(比如 401 响应应触发特定错误类)


















