代码插桩
本章介绍前端测试中代码插桩的基本概念、常见插桩方式,以及在单元测试和端到端测试(E2E)中的实际应用方案。Canyon 的覆盖率能力依赖于稳定、可回溯的插桩结果,因此理解插桩机制是使用 Canyon 的前提。
什么是代码插桩
代码插桩(Code Instrumentation),是指在源码或构建产物中插入“探针代码”,用于记录代码在运行过程中是否被执行、执行次数等信息。
覆盖率工具正是依赖这些探针,在测试结束后统计出:
- 哪些文件被执行过
- 哪些函数 / 语句 / 分支被执行
- 哪些代码从未被覆盖
无论是单元测试还是端到端测试(E2E),只要需要覆盖率数据,都绕不开代码插桩。
插桩方式分类
在前端领域,主流的代码插桩方式可以分为两类:
1. V8 原生覆盖率插桩
- 基于 V8 引擎
- 在运行时从 字节码执行信息 中获取覆盖率
- 不修改源码或构建产物
- 常见于 Node.js、Chromium 浏览器环境
特点:
- 无需改动构建流程
- 性能开销较低
2. Istanbul 插桩(静态插桩)
- 在 构建阶段 对代码进行 AST 转换
- 通过插入计数器的方式记录执行情况
- 常见实现:
babel-plugin-istanbulvite-plugin-istanbul
特点:
- 插桩发生在源码 → 构建产物阶段
- 结果稳定、可控
- 构建产物体积会变大
E2E 测试中的插桩问题
端到端测试(如 Playwright)与单元测试最大的不同在于:
代码运行在真实浏览器内核中
这会直接影响覆盖率的采集方式。
Playwright + V8 覆盖率的局限
Playwright 支持通过 Chromium 的 V8 接口获取 JS 覆盖率,这在以下场景中是可行的:
- 原生 HTML / CSS / JS
- 未打包、未压缩的脚本
- Demo 或简单页面
但在真实生产项目中,往往存在:
- 构建工具打包(Vite / Webpack / Rspack)
- 代码压缩、混淆
- 多模块合并
- 动态加载
此时:
- 覆盖率基于 打包后的 JS
- 与源码结构严重偏离
- Source Map 还原成本极高
- 结果不可控、不可验证
Istanbul 插桩在 E2E 中的解决方案
在现代 SPA 项目中,推荐在构建阶段使用 Istanbul 插桩,再在 E2E 测试中直接收集浏览器内存中的覆盖率数据。
核心思路
-
构建阶段
- 使用
babel-plugin-istanbul等工具 - 在 AST 转换时插入探针
- 生成带插桩的构建产物
- 使用
-
运行阶段(Playwright)
- 浏览器执行插桩代码
- 覆盖率数据累积在
window.__coverage__
-
测试阶段
- Playwright 从浏览器上下文中读取覆盖率对象
- 上报给 Canyon 做聚合分析