异步代码测试需嵌入开发流程并覆盖竞态条件、时序依赖、错误传播链三类风险;统一工具链配置,强制三段式结构编写,ci/pr中设质量门禁。

异步代码测试不是“跑通就行”的收尾动作,而是要嵌入开发流程的前置保障。关键在于让每个测试用例能真实反映运行时行为、暴露竞态与边界问题,并让团队能快速复现、定位、验证修复效果。
测试目标必须覆盖三类核心风险
只测“结果对不对”远远不够。异步测试需主动覆盖:
- 竞态条件:多个 Promise 并发修改同一状态(如多次点击触发重复请求+状态覆盖)
- 时序依赖:依赖 setTimeout / requestAnimationFrame / 微任务顺序的逻辑(如 UI 更新是否在渲染前完成)
- 错误传播链:reject 是否被正确捕获、是否中断后续流程、是否触发 fallback 渲染
测试工具链统一配置与约束
避免本地能过、CI 失败,或不同成员用不同 mock 方式导致行为不一致:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
- 禁用全局
jest.useFakeTimers(),所有定时器测试必须显式启用并jest.runAllTimers()或advanceBy控制进度 - 网络请求统一拦截:使用
msw(Mock Service Worker)替代jest.mock('fetch'),确保跨环境行为一致且可调试 - 禁止裸写
await new Promise(resolve => setTimeout(resolve, 100))等魔法数字等待;必须封装为waitForNextTick()、waitForPaint()等语义化辅助函数 - 所有测试文件顶部强制导入
import '@testing-library/jest-dom'和import { cleanup, render } from '@testing-library/react',避免遗漏清理导致内存泄漏
测试编写必须遵循“三段式结构”
每个异步测试用例需明确分离准备、触发、断言阶段,不可混写:
- 准备阶段:mock 数据、设置初始状态、启动 msw handler,不执行任何 await
-
触发阶段:调用待测函数(如
handleSubmit()),立即返回 Promise,不 await -
断言阶段:使用
await waitFor(() => expect(...).toBeInTheDocument())或await expect(...).resolves.toBe(...),且必须带超时({ timeout: 1000 })
示例:验证提交后显示 loading 再显示 success
test('shows loading then success after form submit', async () => {// 准备
server.use(rest.post('/api/submit', (req, res, ctx) => res(ctx.delay(200), ctx.json({ ok: true }))));
const { getByText, queryByText } = render();
// 触发
fireEvent.click(getByText('Submit'));
// 断言
expect(queryByText('Loading...')).toBeInTheDocument();
await waitFor(() => expect(getByText('Success!')).toBeInTheDocument(), { timeout: 1500 });
});
CI 与 PR 流程中嵌入异步测试质量门禁
把问题卡在提交前,而非上线后:
- PR 标题必须含异步关键词,如 “[async] 修复订单页并发提交重复创建”
- CI 脚本强制运行
jest --detectOpenHandles --verbose,拒绝存在未关闭 Promise 或定时器的构建 - ESLint 自定义规则检查测试文件中是否缺失
await、是否误用done()回调、是否在 describe 块中写 await(应仅限 test 块) - 每季度同步《异步测试反模式清单》,例如:“在 beforeEach 中 await 初始化”、“用 toBeInTheDocument() 替代 waitFor + toBeInTheDocument() 导致 flaky”










