mcpField
M
migrate-to-e2e

migrate-to-e2e

github.com/tester-army测试与质量12.1k139 次安装今天更新
代码仓库

关于 migrate-to-e2e

migrate-to-e2e 是将现有测试套件分批迁移到 e2e 的 Agent 技能。它保留旧套件、用 `MIGRATION.md` 记录进度,并依据源测试框架的迁移指南安排移植。

主要功能

  1. 01识别支持的源测试框架
  2. 02盘点测试与运行配置
  3. 03构建并验证 e2e 测试环境
  4. 04分批移植测试
  5. 05在 `MIGRATION.md` 中跟踪进度

适用场景

  1. 01逐步迁移 Playwright 测试套件
  2. 02移植 Cypress 或 Selenium 测试
  3. 03将 Detox 或 Maestro 测试迁移到 e2e
  4. 04跨会话继续测试迁移

MCP 相关

该技能指导在移植测试时使用 e2e MCP 工具(如 `open_session`、`observe` 和 `locate`)检查应用并确定定位器。若客户端没有这些工具,则改为运行测试并查看其跟踪页面。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tester-army/e2e --skill migrate-to-e2e

安装后在 Claude Code 中输入/migrate-to-e2e确认已加载。

详细介绍

将测试套件迁移到 e2e

迁移从来不是一次性改动。每次会话迁移一个部分,验证它,并确保两个测试套件都通过:旧运行器保留原有的所有测试,新 e2e 测试也能并行通过。进度文件 MIGRATION.md 会记录哪些内容已迁移、还剩哪些内容,以及迁移原因,这样下一次会话无论由你还是其他 Agent 进行,都能从这里继续,而不必从头开始。何时移除旧测试,由用户决定。

两份参考资料就能解决大部分问题。请阅读,不要重复转述。

  • e2e 技能文档涵盖运行器的所有操作:运行 npx e2e init 或 npx skills add tester-army/e2e 后,查看 .agents/skills/e2e/SKILL.md;也可以通过已安装的包运行 npx e2e guide <topic>。下文提到的主题(setup、writing-tests、agent、mcp、running、debugging)都是其中的参考文件。
  • 源运行器的迁移指南,以及目标引擎页面。迁移指南会将运行器的配置、命令和 API 映射到 e2e,并说明 e2e 尚不支持的功能(标记为 missing 或 "No equivalent")及当前的变通方法。这两份文档都随已安装的包提供,位于 node_modules/e2e/docs/ 下,路径与 .mdx 文件相同。

先检测源运行器,再完整阅读其所在行对应的两份文档,然后清点测试,最后编写代码。后续每个步骤都会用到这些文档。

源运行器检测依据迁移指南引擎
Playwrightplaywright.config.*、@playwright/testmigrate/playwrightweb
Cypresscypress.config.*、cypress/ 文件夹migrate/cypressweb
Seleniumselenium-webdriver、pom.xml、build.gradle 或 requirements.txt 中的 Seleniummigrate/seleniumweb
Detox.detoxrc*、package.json 中的 detoxmigrate/detoxmobile
Maestro以 appId: 和 --- 开头的 YAML 流程、.maestro/、脚本或工作流中的 maestro testmigrate/maestromobile

WebdriverIO 和 Appium 没有迁移指南。使用 Selenium 指南映射其 WebDriver 调用;在 mobile 引擎上,使用 Detox 和 Maestro 指南映射 Appium 的设备命令。请告知用户这种映射并不完整。包含两个源运行器的项目应一次迁移一个,每次更新进度文件中的一个部分。

1. 继续或开始

先在 e2e 配置文件旁查找 MIGRATION.md,然后再查仓库根目录。找到该文件则表示这是后续会话:

  1. 阅读该文件,然后阅读其 Source 行所指明的迁移指南和引擎页面。除非用户另有指定,否则其 Next 部分会指明本次会话要处理的部分。
  2. 根据仓库内容核对文件,确认无误后再信任其中的信息:每个 ported 测试仍存在于两个测试套件中,文件中列出的每个源文件或测试仍然存在(有意删除的 retired 项除外),且没有遗漏任何新增的源文件或测试(将其添加为 todo)。若没有 Defect check: 行,请按步骤 2 提问。如果 Next 中注明了一个预先植入的 bug,请先还原它(步骤 6)。
  3. 运行一次文件中的 Run new 命令。如果某个已迁移的测试现在失败,应先修复它,再处理任何新的部分。
  4. 除非进度文件的 Harness: 行写着 done,否则转到步骤 3“构建测试框架”。若为 done,则转到步骤 4“选择要迁移的部分”。

没有进度文件表示这是首次会话:完成步骤 2 和 3,然后继续其余步骤。

2. 清点测试套件(首次会话)

根据仓库回答以下问题;只询问用户你无法观察到的信息。

问题答案所在位置决定事项
源运行器上面的检测表本表之前需要阅读的两份文档
测试每个规范、流程或测试文件,以及其中的标题、标签和按平台或浏览器划分的分支。使用运行器自带的列表或试运行模式统计测试数量,不要通过 grep 统计,因为它会漏掉在循环中创建的测试。npx playwright test --list 会为运行该测试的每个项目各列出一次,因此同一个测试即使列在多个项目下,也只统计一次进度文件中的行,每个源文件一行。现在统计每个文件的断言数量,以便检查移植后的数量:明确的断言(expect、should、assertVisible),以及在运行器的步骤本身就是检查的情况下(例如,目标不存在时 Maestro 的 tapOn 会失败),统计这些步骤
共享代码页面对象、自定义命令、子流程(runFlow: file.yaml)、fixture、钩子、globalSetup在第一个需要它们的测试之前,先移植一次这些辅助代码
运行方式运行测试套件的 CI 任务及其调用的脚本:构建、启动的服务和模拟服务器、数据植入和重置调用、env 文件、设备或浏览器、端口端到端测试运行环境。CI 任务通常是唯一完整的说明;请逐行阅读
端到端测试的位置带有 package.json 的 JavaScript 或 TypeScript 项目:其根目录(在 monorepo 中,与源运行器配置文件同级),测试位于 tests/**/*.e2e.ts。原生、Java 或 Python 项目:应用旁边的 e2e/ 文件夹,其中有自己的 package.json。然后查看源运行器的文件匹配模式(testDir、testMatch、specPattern、流程文件夹)端到端测试配置和进度文件的位置。两个运行器不得收集对方的文件:如果匹配模式重叠,请收窄其中一个。Playwright 测试套件通常已将文件命名为 *.e2e.ts,此时目录是唯一的区分方式:Playwright 的 testDir 下不能有端到端测试,端到端测试的 tests glob 下不能有 Playwright 文件。对比 npx playwright test --list 与 npx e2e list
旧测试套件能否在此运行已安装的源运行器、所需的构建、服务决定是否通过运行旧测试(步骤 6)检查一致性,还是仅根据源文本检查

现在编写 MIGRATION.md(格式见步骤 8),将每个源文件标为 todo。

询问每个切片是否也应针对预先植入的缺陷运行(步骤 6),并记录答案:Defect check: on 或 off。

3. 构建测试支架(一次性)

测试支架包括配置、应用启动和一个通过的测试。完成后,在进度文件中标记完成;后续会话跳过此步骤。

  1. 如果没有 e2e 配置:运行 npx e2e init --yes(主题 setup);对于设备项目,添加 --engine mobile。删除或重写示例测试。源测试套件覆盖的每个平台或浏览器都需要一个目标。
  2. 询问用户 Agent 步骤使用哪种模型,并进行配置(主题 agent)。纯字面量端口不需要模型:每个测试都可以是确定性的,而且没有模型的配置会运行这些测试。
  3. 移植 CI 任务执行的编排,复用仓库自己的脚本,不要编写新脚本:
    • 为应用提供服务的唯一进程(开发服务器、Expo 开发客户端使用的 Metro)成为目标的 app.command,并配置 readyUrl(主题 setup)。该命令只能获取 PATH、HOME、临时目录变量和 command.env,而不能获取 shell 的环境变量;因此,旧运行器环境中服务器读取的变量要通过 command.env 传入。
    • e2e 没有 services 键。数据库、队列和模拟服务器要在运行前通过 CI 任务使用的命令启动;或者让 app.command 运行一个启动这些服务后再为应用提供服务的脚本。在进度文件中记录该命令。
    • 源测试对每个测试执行的重置或填充操作(使用 http.post 的 runScript、cy.task('db:reset')、beforeEach 中的 API 调用)要改为在 beforeEach 中使用 fetch,或使用 fixture,以确保每个测试仍从已知数据状态开始。
    • 导入应用自身代码的填充辅助程序(ORM 工厂、服务器模块)应直接复用,而不是重写。旧运行器可能已将其编译为 CommonJS;e2e 会将每个 .ts 文件作为 ES 模块加载。此时将其导入测试可能会失败,并报错 __dirname is not defined、"does not provide an export named"(针对 CommonJS 包),或 ERR_UNSUPPORTED_DIR_IMPORT。遇到这种情况时,使用仓库自己的 TypeScript 运行器(ts-node、tsx),在子进程中原样运行该辅助程序,并从 fixture 调用它。不要为了让它们成功加载而修改应用模块。
    • 环境变量值要变为模块常量;密码和令牌放在 credentials 或 secrets 下(主题 setup)。如果旧配置通过 dotenv.config() 加载环境文件,就在 e2e 配置中以相同方式加载,这样测试和辅助程序就能看到相同的值。app.command 看不到这些值;服务器需要的变量要通过 command.env 传入。
  4. 将最简单的源测试(通常是登录或启动测试)移植为冒烟测试。其他测试移植前,该测试必须在每个目标上都通过。如果无法通过,问题出在测试支架,而不是测试:修复测试支架。
  5. init 会添加 test:e2e 脚本(e2e run),除非已有同名脚本。如果该名称已被占用,或未运行 init,就使用一个未占用的名称添加脚本。保留旧测试套件的脚本,不作更改。
  6. 在进度文件中标记测试支架已完成,并记录启动依赖项、运行旧测试套件和运行新测试套件的命令。

4. 选择范围

范围是指本次会话可以完成并通过的内容:一个文件、一个文件夹、一个标签或一个功能区域,通常包含 1 到 5 个测试。询问用户希望选择哪一项。如果用户让你决定,请按以下顺序建议:

  1. 共享辅助程序已移植的测试。
  2. 在旧运行器中经常失败的测试:重试、CI 中不稳定的历史记录、解释运行器变通方法的注释。这些测试能从移植中获益最多。
  3. 需要使用指南标记为 missing 或 "No equivalent" 的功能的测试,最后处理。

开始前,将所选范围写入进度文件的 Next 部分,以便会话中断时留下记录。

5. 移植切片

先将切片中的共享辅助函数移植一次,写成普通的 .ts 文件(而不是 .e2e.ts,以免运行器收集这些文件),例如放在 tests/support/ 下。子流程可改为函数或 test.extend fixture;页面对象可改为 fixture(主题:writing-tests)。在进度文件中记录每一项。

然后逐个移植测试:

  • 保留测试标题,并在文件开头添加注释,注明来源 (// Ported from __e2e__/flows/login.yml),以便在报告和评审中相互对应。
  • 首先严格按指南中的表格逐步直译,每个源步骤对应一次带 await 的调用。每条源断言都对应一条独立的 e2e 断言。超时后会失败的等待(waitForFunction)也要多算一条,因此 e2e 断言数量至少应与清单中的数量相同。
  • 源测试中跳过的测试也要完整移植,并继续保持跳过状态,同时保留源测试中的原因,以便在任一套件中重新启用。该测试对应的行要注明这一点;如果原因允许,取消跳过运行一次,并记录结果。
  • 编写定位器前,先确认准确名称:使用 e2e mcp,配合 open_session、observe 和 locate(主题:mcp)。如果客户端没有 e2e MCP 工具,就运行测试并查看其跟踪页面上列出的屏幕(主题:debugging);不要搭建桥接。指南中关于选择器的备注列出了大多数移植所需的修改:e2e 会匹配完整字符串,且匹配到多个结果时会失败,而旧运行器会取第一个结果。如果查询还匹配到一个隐藏副本(另一种布局中的副本),则添加 { visible: true }。如果有两个可见节点匹配,例如第二条提示消息覆盖了第一条,就选择与该步骤相对应的节点。
  • 睡眠或固定等待要改为对所等待状态调用 agent.waitFor(agent.waitFor('the upload finished'));如果定位器可以定位该状态,或配置中没有模型,则改为使用带 timeout 的断言。不要移植等待时长。
  • 按平台分支要改为在测试上设置 { platforms: [...] },或在测试内对 platform fixture 使用 if。
  • 源注释中说明的变通方法(不稳定的 hideKeyboard、坐标点击、围绕选择器的重试循环)是为旧运行器准备的。先尝试直接移植步骤;只有 e2e 也需要时才保留变通方法,并添加单独的注释说明原因。
  • 对于指南标记为 missing 或“No equivalent”的步骤,使用指南中的变通方法。如果没有变通方法,就让测试留在旧运行器中:标记为 stays 并说明原因,然后提出发送一次 npx e2e feedback --type feature -m "<the gap>";先用 --dry-run 预览,只有用户同意后才发送(e2e skill,Feedback)。
  • 不要修改应用代码。测试需要应用中不存在的测试 id 时,应使用 agent.act 或其他定位器;应向用户提议修改应用,而不是自行修改。缺陷检查中植入的 bug(步骤 6)除外,但必须在同一会话中还原。

只有在直译移植通过后,才能使用 agent.act,不得以它代替直译;并且仅在配置了模型时使用。对于源测试中反复打补丁才能完成的步骤,建议使用它:坐标点击、选择器反复转动、在向导中执行一长串点击。随后立即使用定位器断言固定结果(主题:agent)。

6. 证明每个迁移

只有当迁移后的测试与源测试同样严格,且每次运行都通过,才算完成。

  1. 在源测试覆盖的每个目标上运行: npx e2e run <file> --repeat-each 3;如果包含 Agent 步骤,还要加上 --no-cache。每个目标都必须 3 次全部通过,否则就不算完成(主题 running)。
  2. 如果旧测试套件能在这里运行,就在同一构建上运行源测试。 两个运行器都显示的失败属于应用或环境缺陷:报告该问题,不要通过迁移绕过它。 只有 e2e 显示的失败才属于迁移问题。
  3. 对照源测试检查严格性:源测试中的每项断言都必须有对应的 e2e 断言,因此断言数量至少要达到清单中的数量,并且必须检查源测试检查过的每个最终状态。绝不能 弱化断言、无故延长超时时间,或删除步骤来让测试通过。无法如实通过的测试应保持为 blocked,并注明失败原因。
  4. 修改测试前,先阅读任何失败对应的跟踪页面 (主题 debugging)。
缺陷检查(可选)

启用 Defect check: on 时,证明两个套件都会因同一个缺陷而失败:

  1. 征得用户同意后,在源测试断言过的最终状态中植入一个小缺陷(例如结账页显示了错误的总额),并确保所改文件没有未提交的更改。将该文件记录在 Next 下,直到还原为止。
  2. 重新构建,确认应用显示了该缺陷,然后在相应测试片段上运行 Run old(如果该测试能在这里运行),以及 npx e2e run <files> --no-cache。
  3. 仅还原该文件(git checkout -- <file>),重新构建,并确认 git diff -- <file> 为空。
  4. 两者都在检查植入状态的步骤失败:记录结果。只有旧套件通过:记录源测试较弱。只有 e2e 通过:迁移丢失了断言,因此尚未完成。两者都未失败:选择另一个缺陷。在该步骤之前发生的失败不算捕获到缺陷。
  5. 填写该行的 Defect check 单元格,例如 total +1: both fail;如果旧套件无法在这里运行,则填 old not run。

7. 运行两个套件

在用户退役某项源测试之前,两个运行器都要保留它。

  • 已迁移的测试也要保留在旧套件中。只有用户确认后才能删除源测试,然后将其标记为 retired。当剩余的源测试和 e2e 代码都不再使用某个源辅助函数后,提出删除它,并在用户确认后删除。
  • CI:有几个迁移已通过后,提议将 e2e 套件添加到旧作业旁边;由于这会修改旧运行器的工作流,只有用户同意后才进行更改。复用其构建、服务和设备。两个运行器绝不能同时控制同一台设备或模拟器;应在同一作业中先后运行,或为每个运行器分配独立设备。e2e 作业会运行每项已迁移的测试,因此之后的更改若导致某项测试失败,拉取请求也会失败(主题 running,Continuous integration)。
  • 当最后一项源测试被标记为 retired 或 stays 时,进度文件应说明迁移已完成,以及(如有)仍保留的测试。

8. 保留进度文件

MIGRATION.md 与 e2e 配置文件放在一起,是迁移状态的记录。随着进展及时更新,不要拖到最后;即使会话中断,文件也必须如实反映当前状态。

# Migration to e2e

Source: Maestro, `__e2e__/flows/**` (guide: https://e2e.tester.army/docs/migrate/maestro.md, engine: https://e2e.tester.army/docs/mobile.md)
e2e: `e2e.config.ts`, tests `tests/**/*.e2e.ts`
Start dependencies: `pnpm e2e:mock-server`
Run old: `maestro test __e2e__`
Run new: `npx e2e run`
Harness: done, smoke `tests/login.e2e.ts` on ios and android
Defect check: on

| Source | e2e | Status | Assertions (source → e2e) | Passing | Defect check | Notes |
| --- | --- | --- | --- | --- | --- | --- |
| flows/login.yml | tests/login.e2e.ts | ported | 3 → 3 | ios 3/3, android 3/3 | wrong screen: both fail | |
| flows/create-account.yml | | blocked | 6 → | | | date picker spin fails on android, trace in .e2e/results |
| flows/search-screen.yml | | todo | 4 → | | | |

## Shared helpers

| Source | e2e | Used by |
| --- | --- | --- |
| setupApp.yml | tests/support/app.ts `openSignedOut()` | login, create-account |

## Gaps

- `addMedia` has no e2e equivalent (guide: missing); feedback sent.

## Next

flows/composer.yml and flows/composer-self-label.yml (both need `signInAlice()`).

每个源文件一行。如果一个文件中的测试最终状态不同,则每项测试各占一行,并分别记录断言数量。即使套件包含数百项测试,也仍按文件逐行记录:每次会话开始时都会查看此表。

状态:todo;ported(在 e2e 中通过,源测试仍可运行);retired(用户已确认,源测试已删除);blocked(已尝试,但尚未通过,并注明失败原因);stays(有意保留在旧运行器中,并说明原因)。

9. 交接

告知用户本次会话完成了哪些迁移,以及验证了什么:已迁移的测试及其在各目标上的通过次数、缺陷检查结果、已迁移的辅助函数、哪些内容受阻或保留及其原因、已报告的差距,以及下一批迁移内容。询问哪些标记为 ported 的源测试可以退役。列出创建或更改的每个文件。

规则

  • 每个会话迁移一个切片。保持两个测试套件都通过:旧测试套件像之前一样通过,所有 ported 测试也通过。
  • 未经用户确认,不得删除或编辑源测试、辅助程序或旧测试运行器的配置。
  • 根据仓库内容进行迁移。进度文件中列出的每条命令都至少运行一次;若某条命令无法在此处运行(旧测试运行器未安装),请在进度文件中说明。
  • 如果源步骤的含义不明确,例如点击已禁用的控件,或点击可能未命中的坐标,则在迁移中保留其意图。在进度文件中记录你的理解,并询问用户。
  • e2e 技能或指南中的一句话胜过你自己写的一段话。注明主题或指南的章节。
  • 切勿在测试、配置或进度文件中写入机密。注明凭据(主题为 setup)。
  • 不要代替用户提交更改。

评论

支持 Markdown