E2E 测试是什么?学 Harness 工程时第一次搞懂它

E2E 测试是什么?学 Harness 工程时第一次搞懂它

DONE.md 里有一行不起眼的 checkbox:「E2E smoke 测试通过」。我盯着它看了半天,不知道 E2E 是什么,更不知道它为什么跟编译、单元测试并列为「完成标准」。这篇文章把当时搞明白的东西从头讲一遍。


目录

  • DONE.md 里的一行代码
  • E2E 到底是什么
  • 为什么单元测试不够
  • E2E 测试家族:Smoke / Sanity / 全面 E2E
  • E2E 在 Harness 工程中的位置
  • 常用工具速览
  • 第一个 E2E 测试长什么样
  • 写在最后

DONE.md 里的一行代码

前阵子在学 Harness 工程,读到项目的完成标准文件DONE.md。这个文件的作用很简单:列一个 checklist,所有条目都打勾了,任务才算完成。

功能完成标准大概长这样:

- [ ] 编译通过(mvn clean package -DskipTests 无报错) - [ ] 单元测试通过(mvn test -pl ruoyi-business) - [ ] 新增功能有对应测试(TDD:先写测试再写实现) - [ ] 前端构建通过(npm run build:prod) - [ ] E2E smoke 测试通过(npm run test:smoke) - [ ] 涉及数据库变更的,SQL 增量脚本已更新到 sql/

前几条我都认识:编译、单元测试、前端构建,都是日常在做的。但第五条「E2E smoke 测试通过」,我第一次见。

E2E 是什么?Smoke 又是什么?为什么它跟编译、单元测试并列,是完成标准的硬性要求?

查了一圈资料之后,搞明白了。这篇文章把 E2E 测试从零讲一遍,面向跟我当时一样「听过但说不清楚」的开发者。


E2E 到底是什么

E2E 是 End-to-End 的缩写,中文叫「端到端测试」。

一句话解释:站在真实用户的角度,模拟操作流程,验证整个系统从头到尾能不能跑通。

「端到端」的两个「端」分别是:

  • 前端:用户看到的界面、点击的按钮、输入的表单
  • 后端:服务器处理请求、查数据库、返回结果

中间经过的所有环节——前端渲染、API 调用、后端逻辑、数据库读写——E2E 测试全部走一遍。

拿一个具体的业务场景举例。用户在运维管理平台上提交一条问题记录:

用户打开问题列表页 → 点击「新增问题」按钮 → 填写问题描述、选择试验点 → 提交表单 → 前端发 POST 请求到后端 API → 后端 Service 处理业务逻辑,写入数据库 → 返回成功响应 → 前端刷新列表,新记录出现

E2E 测试就是用代码模拟上面这整条链路。它不管你后端用了什么框架、前端用了什么组件库,它只关心一件事:用户操作之后,预期的结果有没有出现。

用 ASCII 画一下数据流:

┌──────────┐ HTTP ┌──────────┐ SQL ┌──────────┐ │ 用户操作 │ ─────────▶ │ 后端 API │ ─────────▶ │ 数据库 │ │ (前端) │ ◀───────── │ (Service) │ ◀───────── │ (MySQL) │ └──────────┘ 响应结果 └──────────┘ 查询结果 └──────────┘ E2E 测试验证:这条完整链路能不能跑通,结果对不对

这跟单元测试不一样。单元测试只测一个零件——比如 Service 层的某个方法,输入 A 是不是返回 B。E2E 测试测的是整辆车——从用户踩油门到车子跑起来,中间每个环节都得对。


为什么单元测试不够

你可能会问:项目已经有单元测试了,为什么还要 E2E?

单元测试验证的是零件质量,E2E 验证的是整车能不能开。零件都合格,不代表装在一起能跑。

说一个我遇到过的真实场景。一个后端模块,Service 层的单元测试全绿——每个方法单独测都对。但前端调接口的时候,返回格式跟前端期望的不一致。单元测试没发现这个问题,因为它测的是 Service 内部逻辑,不涉及 API 返回格式。E2E 测试能发现,因为它走的是完整的「前端请求 → 后端处理 → 返回结果」链路。

在 AI Coding 的场景下,这个问题更突出。AI 写代码的特点是:每个零件可能都对,但组装起来容易出问题。比如 AI 给后端加了一个接口,字段名用驼峰,前端调的时候用了下划线。两边的单元测试各自通过,合在一起就报错。E2E 测试从用户视角走一遍,这种跨层的问题一眼就能看出来。

单元测试告诉你「这个方法对不对」,E2E 测试告诉你「这个功能能不能用」。


E2E 测试家族:Smoke / Sanity / 全面 E2E

E2E 不是一个单一概念,它下面分了好几种。你看到的 DONE.md 里写的是「E2Esmoke测试」,不是「E2E 测试」。这个 smoke 前缀有讲究。

测试类型测什么目的什么时候跑
E2E Smoke 测试系统关键端到端流程(如登录→下单→支付)快速验证核心功能是否可用每次代码提交、部署后,或生产环境定时监控
全面 E2E 测试系统大部分或全部业务流程全面验证各业务流程的功能正确性测试阶段或发版前,耗时较长(10-30 分钟)
冒烟测试 (Smoke Test)最基本、最核心的功能点确保被测版本「可测」,不会让后续测试阻塞新版本提交后,正式测试开始前
健全测试 (Sanity Test)被修改或新增的特定功能模块快速验证修复或新功能是否破坏了原有功能收到修复补丁或新功能后

这四个容易搞混,核心区别在范围和时机

  • Smoke:范围最小,跑最快,回答「系统活着吗」
  • Sanity:范围稍大,回答「这次改动有没有搞坏别的」
  • 全面 E2E:范围最大,回答「所有功能都正常吗」

为什么 DONE.md 里用的是 Smoke 而不是全面 E2E?因为 Smoke 是部署门禁——每次代码提交或部署后立刻跑,几分钟内出结果。如果 Smoke 都过不了,后面的全面测试不用跑了,直接打回。

Smoke 测试的两个核心场景:

场景一:部署门禁。代码部署到测试环境或生产环境后立刻跑 Smoke。通过了才允许继续;失败了直接阻断,避免浪费后续的测试资源。

场景二:生产监控。在生产环境每 5-15 分钟定时跑 Smoke。相当于 7x24 小时的自动巡检,核心功能一出问题立刻告警。

Smoke 测试测试环境CI/CD开发者Smoke 测试测试环境CI/CD开发者alt[测试通过][测试失败]代码提交自动部署触发 Smoke 测试✅ 核心功能正常,继续后续流程❌ 阻断!核心功能异常,停止发布

Smoke 测试的价值在于:用最小的成本,最快地拿到核心功能是否正常的反馈。不追求覆盖面,追求速度和确定性。


E2E 在 Harness 工程中的位置

回到最开始的 DONE.md。再看一遍那几条功能完成标准:

1. 编译通过 ← 代码能编译 2. 单元测试通过 ← 零件没问题 3. 新增功能有对应测试 ← TDD 保障 4. 前端构建通过 ← 前端能打包 5. E2E smoke 测试通过 ← 整车能跑

这五条不是随便列的,它们有顺序:从零件到整车,从局部到整体。E2E Smoke 是最后一道闸门——前四条全过了,E2E 再验证一次完整的业务流程,全过了才算完成。

在 Harness 工程的 9 步开发流程中,E2E 测试出现在第 6 步「集成验证」:

编译 + 单测 + E2E Smoke

第0步 准备

第1步 探索

第2步 提案

第3步 审核

第4步 拆任务

第5步 TDD实现

第6步 集成验证

第7步 代码审查

第8步 归档

第 5 步 TDD 实现写完代码后,第 6 步跑集成验证——编译、单元测试、E2E Smoke 一起上。全部通过才能进入第 7 步的代码审查。

这个设计的逻辑很清晰:AI 写的代码,零件级验证(单元测试)和整车级验证(E2E)缺一不可。单元测试保证每个方法对,E2E 保证整个功能能用。Harness 把这两层验证都写进了完成标准,不给 AI「只写单元测试就交差」的机会。

E2E Smoke 不是可选项,是完成标准的硬性要求。没跑过 E2E,任务就不能算完成。


常用工具速览

写 E2E 测试需要自动化工具。主流的有三个:

工具一句话定位适合场景
Playwright微软出品,API 现代,多浏览器支持,可靠性高新项目首选,适合大部分 Web 项目
Cypress对前端框架友好,调试体验好React / Vue 项目,前端团队主导
Selenium老牌工具,生态成熟,支持语言多遗留项目、需要跨语言集成

新手推荐从 Playwright 开始。理由很简单:API 设计现代(async/await 风格),自动等待机制减少了 flaky tests(时好时坏的测试),支持 Chromium / Firefox / WebKit 三种浏览器,社区活跃文档齐全。

如果你的项目是 Vue 3 + Spring Boot(跟我学 Harness 时的项目一样),Playwright 是最省心的选择。它不关心你前端用什么框架,只管打开浏览器、模拟操作、检查结果。


第一个 E2E 测试长什么样

用一个伪代码例子展示 E2E 测试的核心结构。假设我们要测用户登录流程:

test('用户登录成功后能看到首页',async({page})=>{// 第一步:打开登录页awaitpage.goto('http://localhost:3000/login');// 第二步:填写表单awaitpage.fill('#username','admin');awaitpage.fill('#password','123456');// 第三步:点击登录按钮awaitpage.click('#login-btn');// 第四步:断言结果awaitexpect(page).toHaveURL('/dashboard');awaitexpect(page.locator('.welcome-msg')).toContainText('欢迎');});

四个步骤对应 E2E 测试的核心要素:

步骤作用对应操作
打开页面进入测试起点page.goto()
模拟操作填表单、点按钮page.fill()/page.click()
等待响应系统处理请求Playwright 自动处理
断言结果验证预期是否达成expect()

注意看这段测试代码——它写的是用户行为,不是代码实现。它不知道后端用了 Spring Boot 还是 Express,不知道数据库是 MySQL 还是 PostgreSQL,不知道登录逻辑用了 JWT 还是 Session。它只知道:输入用户名密码、点登录、应该跳转到首页。

这就是 E2E 测试的特点:从用户视角出发,验证的是业务结果,不关心技术实现。如果哪天后端换了框架,只要用户操作流程和结果不变,E2E 测试不需要改。


写在最后

回到最开始那个问题:DONE.md 里的「E2E smoke 测试通过」到底是什么意思?

现在清楚了:它是端到端冒烟测试,站在用户角度走一遍核心业务流程,验证系统整体能不能正常工作。它不追求覆盖面,追求的是速度——几分钟内告诉你核心功能是否「活着」。

在 Harness 工程的完成标准里,E2E Smoke 是最后一道闸门。编译通过、单元测试通过、前端构建通过之后,E2E Smoke 再验证一次完整的业务链路。全过了,任务才算完成。

如果你也在用 AI Coding 做业务项目,建议把 E2E Smoke 加到你的完成标准里。不需要一开始就写很多,先覆盖最核心的一两条流程就行。比如用户登录、创建一条记录、查看详情——这三个场景跑通了,系统基本不会出大问题。

工具选 Playwright,上手快,文档好,跑起来省心。

E2E 测试的价值就一句话:用最小的成本,最快地告诉你系统核心功能是否正常。