| name | webapp-e2e-testing-v2 |
| description | E2E testing methodology for local web applications — 21 principles across 5 domains
(testing philosophy, data & mock, UI interaction, diagnosis & debugging, reporting).
Pure methodology: no tool commands, no project conventions — teaches the agent how to
think, not what to do. Use this skill whenever the user wants to test a local web app,
validate UI behavior, walk through multi-step user flows, mock API responses for testing,
debug E2E test failures, capture screenshots, or understand E2E testing best practices.
Triggers on: "test this web app", "E2E test", "UI test", "browser test", "验证 UI",
"测试页面", "写个测试", "mock API", "端到端测试", "自动化测试", "帮我测一下",
"test the login", "checkout flow", "registration flow", "dashboard test", or any
mention of localhost testing, form validation, flow verification, or E2E debugging.
|
前端 E2E 测试方法论
你是一个遵循方法论的 E2E 测试专家——不凭直觉操作,而是按原则推理。这不是一份操作手册,而是一份原则清单。阅读每条原则,内化其背后的原因和意图,然后在测试时自主应用。你会发现——每条原则正好对应了一个常见 E2E 失败的根因。
使用说明
这份 skill 怎么用?
- 开始测试前:通读「一、测试哲学」建立正确的测试锚点。遇到输入不完整的请求,参考原则 1 主动澄清。
- 准备 Mock 数据时:参考「二、数据与 Mock 策略」确保数据正确且全方位覆盖注入路径。
- 操作 UI 元素时:参考「三、交互策略」做出正确的交互决策——用什么选择器、怎么填表单、遇到遮挡怎么办。
- 测试失败时:参考「四、诊断与调试」——不要绕过失败,诊断根因。
- 输出结果时:参考「五、报告与输出」确保你的报告对人是可读、可追溯、可信任的。
重要:这份文档不包含任何工具命令或项目结构约定。你需要根据当前项目的实际工具将原则映射为具体操作。
输出目录:测试产物(截图、报告、日志)默认存放于项目根目录下的 docs/e2e-testing-output/。如果用户指定了其他路径,使用用户指定的路径。每次测试在输出目录下创建独立子文件夹(如按日期 + 测试场景命名),避免不同测试的产物混在同一目录中。
示例:一次完整的 E2E 测试推理
以下示例展示原则如何协同工作,不是模板——每次测试的推理路径不同,但思考方式一致。
用户请求:「测一下商品搜索功能」
Step 1 — 澄清意图(原则 一.1):用户说「测搜索」,但没说什么算通过。我主动澄清:
- 验证目标:输入关键词后列表显示匹配商品,结果数 > 0,每项含商品名和价格
- 用户流程:首页 → 点击搜索框 → 输入「手机」→ 等待结果 → 验证列表
- 每步预期:搜索框聚焦后出现 placeholder;输入后下拉出现建议;回车后列表区显示商品卡片
Step 2 — 陈述测试计划(原则 一.1):
「验证搜索结果展示:打开首页 → 输入关键词'手机' → 断言列表显示 ≥1 条含'手机'的商品卡片」
Step 3 — 分析数据依赖(原则 二.1 + 二.4):
- 读源码发现:搜索页组件调用
/api/search?q=手机,响应结构为 { items: [{ productId, title, price }] }
- 发现 localStorage 存了
recent_searches(搜索框下拉建议来源)
- 无服务端注入全局变量
Step 4 — Mock 设置(原则 二.2 + 二.3):
- 拦截
/api/search?q=*,返回 3 条商品(类型一致:price 为字符串 "2999.00")
- 预填充
localStorage.recent_searches = ["手机"]
- 具体路由
/api/search?q=手机 注册在通配符 /api/search* 之前
Step 5 — 交互与验证(原则 三.1 + 三.2 + 一.2):
- 用
aria-label="搜索" 定位搜索框(语义选择器)
- 等待搜索框可见且可交互后输入
- 断言验证页面渲染结果:列表区存在 ≥1 个含文本「手机」的元素
一、测试哲学
测试哲学定义了「测什么」和「以什么标准测」。在执行任何测试动作之前,先在这些原则上建立锚点——否则后续的 Mock 策略、交互策略都没有方向。
原则 1:先理解,再测试
为什么:你不是人类 QA,没有隐性的业务理解。如果用户说「测一下登录」,你必须确认:是新用户还是已有用户?成功登录后应该看到什么?才能写出有效的测试。
如何应用:执行测试前,必须明确三件事——按这个顺序思考:
- 验证目标——怎样算通过?定义具体、可验证的断言(如:「首页显示用户名 '张三' 且顶部导航栏出现『我的订单』入口」)。这是起点:先知道你要验证什么,再推导需要哪些步骤
- 用户流程——达到验证目标需要哪些操作步骤(如:打开登录页 → 输入手机号 → 获取验证码 → 输入验证码 → 点击登录)
- 每步预期状态——每步操作后页面应呈现的中间状态(如:输入验证码后按钮变可点击;登录后跳转到首页)
如果用户输入缺少这些信息,应主动要求澄清,而不是猜测后直接执行。
陈述测试计划:明确三件事后,在执行前用一句话概括测试计划——「验证[目标]:[步骤A] → [步骤B] → 断言[结果]」。在执行过程中始终铭记你要验证什么、怎么验证、期望结果是什么。
常见陷阱:用户说「测一下 XX 页面」就直接打开页面截图——你可能截到的是空白加载态、错误页面或无关弹窗,而非用户真正关心的内容。→ 先澄清验证目标,再执行操作。
原则 2:测试用户可见行为
为什么:E2E 测试的价值在于验证「用户实际看到什么、能做什么」,而非代码内部状态。用户不知道也不关心某个变量是 true 还是 false——他们只关心屏幕上显示了什么。
如何应用:
- 断言验证页面渲染结果(元素可见性、文本内容、交互反馈),而非内部状态变量
- 选择器优先使用用户可感知的属性(文本内容、
aria-label、role、placeholder),而非实现细节(CSS class 名、DOM 结构层级)
常见陷阱:验证 window.__userInfo__.isLoggedIn === true 而不是验证页面显示了用户名和头像。→ 断言用户可见的渲染结果(如「页面包含文本'张三'」),而非内部状态变量。
原则 3:每次测试独立运行
为什么:测试之间的状态污染是 E2E 测试不稳定的头号原因。如果测试 B 依赖测试 A 留下的数据,某天测试 A 失败,测试 B 也会跟着失败——但问题根本不在 B。
如何应用:
- 每次测试运行前,确保浏览器环境和数据状态是干净且已知的
- 如果需要特定数据(如一个已有订单的用户),在测试开始时通过 mock 或 API 创建,而不是假设「数据库中已有这个用户」
- 测试结束后不需要清理状态(下次测试会自己设置初始状态),但不要故意污染共享数据
常见陷阱:假设数据库中已有测试用户,直接使用固定账号登录。→ 每次测试开始时通过 mock 或 API 创建所需数据,确保状态已知且可控。
原则 4:只控制你能控制的
为什么:第三方服务(支付网关、外部 API、CDN 资源)的行为不在你的控制范围内。依赖这些服务会导致:
- 随机失败——第三方宕机了,你的测试就挂了
- 运行缓慢——跨网络等待第三方响应
- 无法修复——第三方的问题你改不了代码
如何应用:对任何不受你控制的网络依赖,使用 mock 替代真实请求。
- mock 数据必须与真实 API 的响应结构一致(对照 API 文档或类型定义)
- 但不能假设第三方服务总是可用的——这是 mock 的意义所在
常见陷阱:测试中包含对 paypal.com 或 google-analytics.com 的真实网络请求。→ 对所有不受控的网络依赖使用 mock 替代,确保测试不依赖外部服务可用性。
原则 5:关键路径优先
为什么:E2E 测试比单元测试慢得多(几秒 vs 几毫秒),不可能覆盖所有边缘情况。试图用 E2E 测试覆盖一切会导致:测试套件臃肿、运行缓慢、团队不再信任测试结果。
如何应用:
- 选择验证一个功能时,聚焦于核心用户流程(主路径),而非每种异常路径
- 异常路径和边缘情况留给单元测试和集成测试
- 一次测试应验证一个完整的用户目标(如「完成一次购买」),而非一个孤立的 UI 操作(如「点击按钮」)
常见陷阱:为一个表单写了 10 个 E2E 测试覆盖各种校验错误提示——表单校验属于单元测试范围,E2E 只需要验证「填写正确数据→提交成功」这一个主路径。
原则 6:先分析,再行动
为什么:写选择器前不分析 DOM 结构、写 mock 前不分析 API 契约,会导致反复返工——选择器匹配错误元素、mock 数据结构不对、路由优先级错误。每次返工都消耗额外的时间和 token。
如何应用:
- 写选择器前:通过源码或截图确认目标元素的真实 DOM 结构(类名、层级、可见性、同类元素区分)
- 写 mock 前:确认 API 的响应结构、路由匹配规则、请求参数编码方式
- 遇到失败时:先诊断根因,而非直接换种方式重试
常见陷阱:凭经验写 button:has-text('Confirm') 然后发现按钮文本为空(i18n 组件未渲染);凭经验写宽泛的 input 选择器然后匹配到 readonly 的装饰性输入框而非真实可交互输入框;凭经验写 mock 数据但路由匹配顺序错误导致 mock 不生效。
二、数据与 Mock 策略
本地 E2E 测试的核心挑战:前端需要数据才能渲染,但后端 API 可能不可用或无法提供目标状态的数据。Mock 策略解决的就是「如何在可控环境下让被测页面呈现目标状态」。
原则 1:先看代码,再定数据
为什么:你凭直觉构造的 mock 数据几乎总是错的。因为 API 响应结构 ≠ 代码期望结构——中间经历了 .map()、.filter()、状态映射等转换。直觉 mock 会导致页面空白或渲染错误。
如何应用:mock 数据之前,必须追踪数据从 API 入口到 UI 渲染的完整路径:
- 定位目标页面组件
- 找到 API 调用点(
fetch()、axios.get()、useQuery() 等)
- 追踪响应数据的转换逻辑——
.map() 做了什么?解构了哪些字段?有条件筛选吗?
- 确认最终渲染所需的字段和类型
常见陷阱:对照 API 类型定义文件直接写 mock 数据,而忽略组件中对数据的二次转换——如组件将 items.map(i => ({ id: i.productId, name: i.title })) 做了字段重命名,你 mock 的 { id, name } 结构根本不会被用到。
原则 2:Mock 的边界是网络层
为什么:E2E 测试的价值在于验证真实的你的前端代码。一旦 mock 了 HTML、替换了组件、或在前端代码中注入了假数据,测试就不再能验证真实代码的正确性——你测的是一个不存在的「假页面」。
如何应用:mock 只能发生在网络请求层——拦截 HTTP 请求并返回伪造的响应。
- ✅ 拦截 API 请求,返回 mock JSON
- ✅ 拦截 HTML 响应,替换内联
<script> 中注入的全局变量(见原则 4)
- ❌ 修改被测页面的 DOM 结构
- ❌ 替换前端组件
- ❌ 通过
page.evaluate() 向浏览器注入假数据来绕过渲染逻辑
常见陷阱:因为列表没渲染出来,直接通过 page.evaluate() 向 DOM 中插入 <li> 元素——你测试的已经不是真实代码了,而且你不知道真实代码为什么没渲染。
原则 3:数据真实性决定测试可靠性
为什么:如果 mock 数据与真实 API 响应在结构、类型、取值范围上差距太大,通过测试的代码可能在真实环境中立即崩溃。mock 数据的目的是模拟「真实但可控」的数据,而非「假到让测试通过」的数据。
如何应用:
- 每个字段的类型必须与真实 API 一致——字符串字段不能传 number,数组字段不能传 null
- 数组长度应反映真实场景——列表至少 2-3 项(真实用户不会只有 1 条数据),不要 mock 1 项来简化测试
- 可能触发条件渲染的字段必须设置正确的值——如
isNewUser: true 应触发新用户引导界面,status: 'expired' 应触发过期提示
- 路由注册顺序影响匹配优先级——应将具体路由注册在宽泛路由之前(如
/api/v1/users/123 先于 /api/v1/users/*)
- URL 参数解码需处理边界情况——如
+ 号在 query string 中代表空格,解码时必须处理
- 兜底 mock 不应过于粗糙——对于未匹配到的请求,返回结构完整的错误响应而非
result: null
常见陷阱:
- 为了让测试通过,把字段值都设成最理想的值——却遗漏了真实 API 返回的
amount: "100.00"(字符串)被你的 mock 设成了 amount: 100(number),导致 .toFixed(2) 调用报错,页面崩溃
- 路由按任意顺序注册,导致具体的 mock 被宽泛的通配符路由覆盖,mock 始终不生效
- 兜底 mock 返回
{ result: null },导致前端解构时抛出 Cannot read property of null
原则 4:状态模拟覆盖所有注入路径
为什么:前端应用的状态来源有三条路径——不是只有 API。遗漏任何一条,页面状态就不会是你预期的。
如何应用:在分析目标页面的状态依赖时,同时检查三条路径:
- API 响应(网络层)——页面加载时调用了哪些 API?通过拦截 HTTP 请求 mock 响应
- 持久化存储——组件 mount 时从 localStorage、sessionStorage、IndexedDB 读取了哪些值?在页面脚本执行之前预填充这些值
- 服务端注入的全局变量——HTML 内联
<script> 中是否有 window.__INITIAL_STATE__、window.__USER_INFO__ 等?通过拦截 HTML 响应替换这些脚本的内容
路径 2 和 3 的预置必须在页面脚本执行之前完成,否则组件已经读取了默认值。
常见陷阱:API mock 全部正确,但页面状态不对。→ 同时检查三条注入路径(API + 持久化存储 + 全局变量),确保页面脚本执行前所有依赖已就绪。
三、交互策略
交互策略不规定「用什么工具」,而是建立「面对一个 UI 元素时应该如何决策操作方式」的思考框架。数据到位后,你需要稳定地操作 UI 元素——这些原则告诉你如何做出正确决策。
原则 1:先用语义定位,再用选择器
为什么:DOM 结构会随重构变化,但文本、角色、标签等用户可感知的属性相对稳定。依赖 CSS 类名或 DOM 层级的选择器是最脆弱的——设计师改个 class 名你的测试就挂了。
如何应用:选择器优先级从高到低:
- 语义属性——文本内容、
aria-label、role、placeholder、title
- 稳定标识——
data-testid 等项目约定的测试专用属性
- 结构选择器——CSS 类名、DOM 层级路径(不到万不得已不用)
- ❌ 永远不要使用 XPath 或基于绝对位置的选择器
关键:文本必须从源码或截图确认,不能凭经验猜测。i18n 组件在 headless 环境下可能渲染为空字符串,应同时准备类选择器作为备用方案。
项目约定优先:如果项目中已有选择器风格约定(如统一使用 data-testid、或所有按钮都用 aria-label),遵循项目现有约定而非坚持上述通用优先级。一致性比个人偏好更重要。
常见陷阱:.click('.btn-primary:nth-child(3)')——设计师调整了按钮顺序,第三个按钮变成了取消而不是确认,测试悄然验证了错误的按钮。
原则 2:操作前先确认元素可达
为什么:元素在 DOM 中存在 ≠ 可交互。它可能在视口外、被其他元素遮挡、处于动画中、或者尚未渲染完成。在不可达状态下强行操作会导致假阳性失败——不是因为功能错了,而是因为操作时机不对。
如何应用:每次交互前,按顺序确认目标元素:
- ✅ 已在 DOM 中渲染(等待元素出现,不是等待固定时间)
- ✅ 在视口内可见(必要时先滚动到元素位置)
- ✅ 未被其他元素遮挡(检查覆盖层、弹窗、iframe)
- ✅ 未处于 disabled 状态
- ✅ 是目标元素本身,而非同类元素——如 Search 组件内可能同时存在 readonly 的 combobox 和真实输入框,确保选择器匹配到正确的那个
等待使用语义条件(如「元素可见」「文本出现」),而非固定时间延迟。
常见陷阱:page.wait_for_timeout(3000) 然后直接操作——3 秒可能在慢网络下不够,也可能在快网络下浪费 2.5 秒。→ 用语义条件等待(如「元素可见」「文本出现」),只在工具不支持语义等待时才用固定延迟。
原则 3:理解受控组件的事件模型
为什么:现代前端框架(React、Vue)的受控组件不监听 DOM 的原生 value 变更,而是依赖框架的合成事件(如 React 的 onChange)。直接修改 DOM 属性不会触发状态更新,导致:按钮保持 disabled、表单校验不触发、数据没有真正「填进去」。
如何应用:与输入框交互时,模拟完整的用户操作序列:
- 聚焦(focus)——点击或 Tab 到输入框
- 设置值(fill/type)——使用工具提供的
fill() 方法,它会触发正确的键盘事件序列
- 失焦(blur/tab)——离开输入框,触发
onBlur 校验
如果填值后按钮仍 disabled:大概率是合成事件未触发——此时改用逐字符输入(type() 而非 fill())来确保每个字符都触发了 onChange。
常见陷阱:用 JavaScript element.value = 'xxx' 或 element.setAttribute('value', 'xxx') 直接设置值——React state 完全没有更新,按钮永远 disabled。→ 使用 fill() 触发完整事件链;若仍不生效,改用 type() 逐字符输入确保 onChange 触发。
原则 4:处理遮挡而非绕过遮挡
为什么:浮层、弹窗、iframe、toast 通知经常在测试关键时刻拦截点击。简单地 force=true 绕过可见性检查会掩盖真正的 UI 问题——弹窗不应该出现、动画尚未结束——导致假 PASS。
如何应用:遇到「元素被拦截」错误时,按优先级处理:
- 先确认拦截层是否应该存在——如果是预期的弹窗/下拉菜单,先正常关闭它(点击关闭按钮、按 Escape)
- 如果是非预期的覆盖层(引导弹窗、营销弹窗、通知),在操作目标元素前关闭它们
- 如果拦截来自浮层 iframe(其透明遮罩捕获了所有鼠标事件),此时才使用合成事件绕过指针检测——这是 iframe 的技术限制,不是被测应用的问题
叠加弹窗的 DOM 残留问题:某些 UI 框架在关闭弹窗后只是将其设为 display:none 而不从 DOM 中移除。此时 .first 可能匹配到已隐藏的弹窗实例。应使用可见性选择器(如 [aria-hidden="false"]、:visible 或 .active)定位当前活跃的弹窗。
force=true 是策略,不是常态。每次使用都应知道原因。
常见陷阱:每次交互都加 force=True 来避免拦截错误——这会在弹窗真正不应该出现时给出假 PASS,你永远不会发现这个 bug。或者使用 .first 定位弹窗时匹配到了已隐藏的残留实例,导致操作失败。
原则 5:表单提交前必须诊断 disabled 状态
为什么:表单填写完毕后提交按钮仍 disabled,是 E2E 测试中最常见且最难诊断的阻塞点。原因多样:合成事件未触发(原则 3)、下拉菜单未关闭(遮挡了点击目标)、radio 选错了实例、或校验未通过。不是重试能解决的。
如何应用:提交前,如果按钮是 disabled 状态,不要重试或等待——立即诊断。按优先级排查:
- 截图确认:下拉菜单是否仍展开?(这是最常见原因——下拉遮住了按钮)
- 确认输入值:UI 上显示的输入值是否正确?React 受控组件的值可能没有真正更新
- 确认选择状态:radio/checkbox 是否选中了正确的选项?(多实例时容易选错)
- 确认校验错误:页面上是否有表单校验错误提示?
定位根因后针对性修复,而非绕过。
常见陷阱:按钮 disabled 就一直 wait_for,等到超时后就直接标记测试失败,不给出任何诊断信息。→ 按「截图 → 确认输入值 → 确认选择状态 → 确认校验错误」顺序排查,定位根因后针对性修复。
四、诊断与调试
E2E 测试中遇到阻塞是常态。这一领域的核心不是「避免失败」,而是「每次失败都是一次诊断机会,并且必须找到根因才能修复」。你的可靠性取决于你面对失败时的反应模式。
原则 1:失败是可诊断的,不可跳过的
为什么:测试失败后跳过或换个方式绕过去,会积累「虚假通过」——测了一个不存在的场景却以为通过了。每次失败都暴露了一个信息缺口(mock 数据不对、选择器错误、状态假设错误),填补这个缺口就是提高测试可靠性的过程。
如何应用:遇到阻塞时:
- 确认什么错了——是元素没渲染、数据没加载、还是操作没生效?
- 定位根因——不要停留在表面症状(如「元素未找到」),追问「为什么没找到」
- 修复根因后重试——而不是换个操作方式碰运气
- 一次只修复一个变量——不要同时改动 mock、选择器和交互逻辑。改完立即验证,确认该改动确实解决了问题,再处理下一个
常见陷阱:「这个元素找不到,换个方式试试」→ 三个方式都试了一遍,都失败了 → 「这个场景测不了」。正确做法:截图看页面状态 → 发现页面根本没渲染目标区域 → 追查发现 mock 数据缺了一个字段 → 补上字段 → 测试通过。
原则 2:分层排查,先近后远
为什么:E2E 失败的可能原因很多,随机猜测浪费大量时间。按「离 UI 从近到远」的顺序排查,效率最高——离 UI 越近的信息越容易获取,也越可能直接指向根因。
如何应用:排查顺序从近到远:
- 浏览器 Console——有 JS 报错吗?(报错信息通常直接指向根因——TypeError、ReferenceError 等)
- 网络请求——API 是否被正确 mock?响应是否返回了预期结构?(检查 Network 面板)
- 数据流——API 响应结构与代码期望结构是否匹配?有没有被
.map() 转换后字段丢失?
- 条件渲染——触发 UI 显示的条件字段值是否正确?(如
isVisible && <Component /> 中的 isVisible)
- 交互状态——元素是否被遮挡、disabled、或在视口外?
常见陷阱:元素没渲染出来,先去检查 Playwright 选择器语法——而根因是 Console 里有一个 TypeError: undefined is not an object,导致整个组件树崩溃了。你花了 10 分钟调选择器,实际上 10 秒钟看一眼 Console 就解决了。
原则 3:诊断结论必须有证据支撑
为什么:你容易在诊断时「猜测」根因——因为某个模式在别的项目见过。但不同项目的根因可能完全不同,猜测会导致虚假修复——这次碰巧对了,下次同样的问题换一个项目又错了。
如何应用:每次诊断结论必须基于以下证据,四者至少有其二:
- 截图——页面的实际渲染状态(不是你以为的状态,是实际的状态)
- 网络日志——请求是否发出、响应是否返回、返回了什么
- Console 日志——是否有 JS 报错、警告
- 源码阅读——条件渲染的具体逻辑、数据转换代码
记录:修改了什么、为什么这样修改。
常见陷阱:「应该是因为数据格式问题」→ 直接修改 mock 数据碰运气。→ 先收集证据(截图 + Console + 网络日志),基于证据定位根因后再修改。
五、报告与输出
测试的最终交付物不是脚本运行成功,而是让用户理解「测了什么、结果如何、如果失败原因是什么」。一份好的报告让人 30 秒内理解全貌,一份差的报告让人花 10 分钟还找不到关键信息。
原则 1:报告回答三个问题
为什么:用户不需要「测试通过」四个字。他们需要知道:测了什么、怎么测的、结论可信吗。缺少这三个答案的报告没有信息量,用户只能自己猜——这比你替他们猜更糟糕。
如何应用:每次测试完成后,报告中必须明确:
- 测了什么——覆盖的用户流程和验证点(「验证了从登录到下单的完整流程,含 3 个验证点:登录成功、商品列表可见、下单按钮可点击」)
- 怎么测的——关键操作序列和数据设置,不是逐行代码,而是「模拟了已登录用户状态(mock API + localStorage),执行了页面加载 → 选商品 → 下单操作」
- 结论可信吗——如果有任何阻止完整验证的限制(如「支付 API 无法 mock,未验证支付成功后的跳转」),必须显式说明
常见陷阱:报告中只有「测试通过 ✓」和一个截图——用户不知道你验证了什么,也不知道你是否漏掉了关键场景。三天后就连你自己也不知道了。
原则 2:失败报告的可追溯性优于详细程度
为什么:测试失败的真正成本不是运行失败本身,而是排查失败原因的时间。一个可追溯的失败报告能让用户在 30 秒内定位问题;一个冗长但不可追溯的报告只会增加排查成本。
如何应用:失败报告中必须包含:
- 失败步骤的截图——页面当前的实际状态(不是操作前的状态)
- 失败时的网络状态——哪个 API 请求未 mock 或返回异常
- 失败前的关键操作序列——「在 XXX 之后,预期 YYY,实际 ZZZ」
- 已尝试的诊断步骤和结论——「Console 无报错,网络请求已被 mock,但元素未渲染 → 推测 mock 数据结构不匹配」
不需要:堆栈跟踪的全文、测试脚本的完整代码。这些可以放在附件中,但报告的正文应该是可读的诊断记录。
常见陷阱:失败后给一个堆栈跟踪和一句「元素未找到」——用户不知道页面当前是什么状态、不知道 mock 是否生效、不知道你是否尝试过诊断。排查成本完全转嫁给了用户。
原则 3:截图是核心证据,不是装饰品
为什么:截图是 E2E 测试中最直接的状态证据。一张有信息量的截图让用户在 5 秒内确认「这是通过还是失败」。但无目的的截图(空白页面、无关区域)不仅无用,还会误导用户——让他们以为某个状态已验证。
如何应用:截图必须满足:
- 截取完整视口——不要截取局部区域,用户需要看到页面整体状态(URL 栏、整体布局)
- 在关键状态变化点截图——操作前(初始状态)、操作后(结果状态)、异常状态(失败状态)
- 如果失败,截图必须包含失败时的实际页面状态,而非操作前的状态
- 截图前确认目标 UI 已渲染完成——等待元素可见或网络空闲后再截图,避免截到加载态或空白状态
- 截图文件命名应描述内容和时间点——如
01-login-form-empty.png、02-after-submit-success.png、03-error-submit-button-disabled.png,而非自动生成的 screenshot_20240424_001.png
- 每个关键状态变化点只保留一张有效截图——多次重试的废弃截图及时清理,避免产物混乱
常见陷阱:在测试脚本末尾截了一张图——不管测试在第 2 步就失败了,截图截的是第 3 步的空白页面。这张图不仅无用,还会误导用户以为「第 3 步是空白」。
原则速查
| 领域 | 原则 |
|---|
| 一、测试哲学 | 1. 先理解再测试 · 2. 测试用户可见行为 · 3. 每次测试独立运行 · 4. 只控制你能控制的 · 5. 关键路径优先 · 6. 先分析再行动 |
| 二、数据与Mock | 1. 先看代码再定数据 · 2. Mock 边界是网络层 · 3. 数据真实性决定可靠性 · 4. 覆盖所有注入路径 |
| 三、交互策略 | 1. 先用语义定位 · 2. 操作前确认元素可达 · 3. 理解受控组件事件模型 · 4. 处理遮挡而非绕过 · 5. 表单提交前诊断 disabled |
| 四、诊断与调试 | 1. 失败可诊断不可跳过 · 2. 分层排查先近后远 · 3. 诊断结论有证据支撑 |
| 五、报告与输出 | 1. 报告回答三个问题 · 2. 失败报告可追溯优先 · 3. 截图是核心证据 |
记住:这些原则不是用来背诵的清单,而是理解 E2E 测试本质的入口。当你遇到一个测试失败,翻到对应的领域,读那条原则的「为什么」——你大概率会发现失败的原因就在那里。