关键不是制造真实断网,而是让被测代码面对明确失败的网络调用并验证响应行为;通过vi.mock/jest.mock拦截axios/fetch,返回匹配生产环境的networkerror(如domexception),精准模拟连接失败、超时、abort等场景,并验证重试、降级、状态更新等业务逻辑。

在单元测试中模拟网络中断,关键不是“制造真实断网”,而是让被测代码面对一个明确失败的网络调用,并验证它是否按预期响应——比如重试、降级、抛错或更新 UI 状态。
用 mock 拦截请求并返回拒绝 Promise
这是最直接、最可控的方式。不依赖真实网络,也不需要启动代理或修改系统设置。
- 用 jest.mock 或 vi.mock 替换 axios/fetch 模块,让其返回
Promise.reject(new TypeError('NetworkError'))或Promise.reject(new DOMException('Failed to fetch', 'NetworkError')) - 确保错误类型与生产环境一致(例如 fetch 抛的是
DOMException,axios 是AxiosError),否则if (err.name === 'NetworkError')这类判断会失效 - 示例(Vitest):
vi.mock('axios', () => ({
default: {
get: vi.fn().mockRejectedValue(new DOMException('Failed to fetch', 'NetworkError'))
}
}))
模拟特定阶段的中断:请求发出但无响应
有些逻辑会区分“连接失败”和“超时”,这时需更精细控制。
- 用 AbortController 配合 mock:在请求发起后立即
abort(),验证代码是否正确处理AbortError - 用 setTimeout + Promise 构造一个永远不 resolve/reject 的请求,再配合
jest.setTimeout(100)让测试超时失败——这能验证你的超时逻辑是否生效(注意:这不是测试“中断”,而是测试“超时兜底”) - 避免用
jest.useFakeTimers()操控网络层时间,它对 fetch/axios 无效;fake timers 只影响setTimeout、setInterval等原生定时器
覆盖真实中断引发的典型行为路径
别只检查“有没有报错”,要验证业务逻辑是否健壮:
- 重试机制:mock 第一次失败、第二次成功,断言函数被调用了两次,且最终结果正确
- 离线状态标记:当请求失败时,是否设置了
isOnline = false并触发 UI 更新(可用jest.spyOn监听状态 setter) - 缓存 fallback:网络失败时,是否读取了 localStorage 中的旧数据并返回
- 用户提示:是否调用了 toast.error() 或更新了 errorText 字段,且消息内容符合设计
不用 node-mitm 或 Sinon.stub 代替模块 mock
虽然 node-mitm 能拦截 TCP 层,Sinon 能 stub 全局 fetch,但它们引入了额外复杂度:
-
node-mitm适合集成测试,单元测试中过度重量,且难以精确控制“哪次请求中断” -
Sinon.stub(global, 'fetch')有效,但不如模块级 mock 清晰(比如无法区分不同 API 的行为),也容易漏掉 restore 导致测试污染 - 优先选择框架原生支持的模块 mock(
vi.mock/jest.mock),语义明确、隔离性强、恢复自动
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











