单元测试中精准模拟promise异常,关键是控制reject行为并确保捕获:用promise.reject()直接构造拒绝态、jest.fn().mockrejectedvalue模拟依赖拒绝、监听unhandledrejection避免误报、验证catch/try-catch中的错误处理逻辑。

在单元测试中精准模拟 Promise 异常,关键不是“让 Promise 抛错”,而是控制 Promise 的 拒绝(reject)行为,并确保测试能捕获它——这要求你绕过异步时序干扰、避免未处理拒绝警告,并与测试框架(如 Jest、Vitest)的异步断言机制对齐。
用 Promise.reject() 直接构造被拒绝的 Promise
这是最直接、最可控的方式。不依赖真实异步逻辑,完全跳过定时器或网络请求,从源头注入拒绝状态:
- ✅ 精准:拒绝值、类型、堆栈都可自定义
- ✅ 同步可控:Promise 构造即拒绝,无竞态风险
- ❌ 不适合测试「异步过程中抛错」的时序细节(如 fetch 超时后 reject)
示例(Jest):
test('fetchUser 应拒绝并返回错误信息', async () => {
// 模拟 API 返回一个立刻拒绝的 Promise
jest.mock('./api', () => ({
getUser: jest.fn(() => Promise.reject(new Error('Network failed')))
}));
await expect(fetchUser(123)).rejects.toThrow('Network failed');
});
用 jest.fn() 返回 reject Promise,验证调用与错误传播
当被测函数内部调用某个 Promise 函数(如 axios.get),你需要模拟该函数返回 reject,并检查错误是否按预期被 catch、转换或透传:
- ✅ 验证函数是否正确调用了依赖项(如传参是否正确)
- ✅ 验证错误是否被 try/catch 捕获、重抛、或转为特定业务错误
- ✅ 支持 spy + mockImplementation 组合,灵活切换 resolve/reject 场景
示例:
test('handlePayment 调用 api.pay 并透传错误', async () => {
const mockPay = jest.fn().mockRejectedValue(new Error('Insufficient balance'));
jest.mock('./paymentApi', () => ({ pay: mockPay }));
await expect(handlePayment({ amount: 100 })).rejects.toThrow('Insufficient balance');
expect(mockPay).toHaveBeenCalledWith({ amount: 100 });
});
避免 unhandledrejection,让测试失败更明确
Jest/Vitest 默认会将未被捕获的 Promise rejection 视为测试失败,但有时因测试清理不及时或异步逻辑残留,会误报。主动监听并清除可提升稳定性:
- ✅ 在 test 前添加全局监听,fail 测试若出现意外 reject
- ✅ 在 afterEach 清除监听,防止跨测试污染
- ✅ 对于需要测试「未处理拒绝」的边界场景,可临时禁用该检查
示例(Jest):
let unhandledRejectionHandler;
beforeEach(() => {
unhandledRejectionHandler = (event) => {
// 让未捕获的 reject 导致测试立即失败
fail(`Unhandled promise rejection: ${event.reason}`);
};
window.addEventListener('unhandledrejection', unhandledRejectionHandler);
});
afterEach(() => {
window.removeEventListener('unhandledrejection', unhandledRejectionHandler);
});
测试 .catch() 或 try/catch 中的错误处理逻辑
很多业务代码会显式处理 Promise 错误。这时不能只测「是否 reject」,而要测「错误是否被正确分类、日志、降级或转为默认值」:
- ✅ 使用 await + expect(...).resolves.toEqual(...) 验证降级结果
- ✅ mock console.error / Sentry.captureException 验证错误上报
- ✅ 注意:不要用 .then(..., err => {...}) 回调风格写测试,易漏 await
示例:
test('loadConfig 失败时返回默认配置', async () => {
jest.mock('./configLoader', () => ({
load: jest.fn(() => Promise.reject(new Error('Config not found')))
}));
const result = await loadConfig();
expect(result).toEqual({ theme: 'light', timeout: 5000 });
});
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











