Playwright Java 测试编写器 是做什么的?
将手动测试用例转换为可维护的 Playwright Java 测试,这些测试看起来像是由框架负责人编写的:并行安全、无 flaky sleep、隐藏定位器的页面对象,以及可追溯到原始测试用例 ID 的 Allure 报告。
工作流程
1. 解析手动测试用例
为每个用例提取:ID、标题、优先级/严重性、前提条件、测试数据、步骤、每个步骤的预期结果。输入形式多样(Excel 行、Jira 导出、Gherkin、自由文本),因此在编写代码前将其标准化为此格式。
如果用例按原样不可自动化(需要真实手机 OTP、视觉判断如“看起来正确”、涉及真钱的第三方支付),请不要伪造。在输出的 未自动化 部分列出,并说明原因和建议(模拟 API、使用测试 OTP、视觉快照比较)。
2. 首先检测现有框架约定
如果用户分享了仓库文件(BaseTest、页面类、testng.xml、pom.xml),完全镜像它们:包名、基类名、命名风格、断言风格、配置读取方式。与其代码库的一致性优于以下参考风格。
如果没有共享任何内容,则使用 references/framework-scaffold.md 中的框架搭建指南。仅当用户要求框架或明显没有框架时,才生成脚手架文件(BaseTest、PlaywrightFactory、BasePage、配置);否则假设它们存在,仅生成页面对象和测试。
3. 设计页面对象
- 每个页面或可复用组件(页眉、购物车抽屉、筛选面板)一个类。组件拥有自己的类,并组合到页面中。
- 定位器为
private final Locator字段,在构造函数中初始化。测试从不直接调用page.locator(...)。 - 页面方法描述用户意图(
login(user, pass)、addToCart(productName)),而非机械操作(clickButton2)。使用 Allure@Step("...")进行注释,并使用参数占位符,使报告读起来像手动用例。 - 导致导航的动作返回下一个页面对象(流畅流)。停留在当前页面的方法返回
this或 void。 - 在添加新方法之前,重用现有的页面方法。如果两个步骤映射到相同的意图,则使用一个方法。
- 通过意图命名的 getter 公开用于断言的状态,返回
Locator(例如errorMessage()),以便测试可以使用 web-first 断言。
定位器优先级(从最稳定到最不稳定):
getByTestId(如果应用程序使用data-test、data-qa等,请配置一次属性名称)getByRole加上可访问名称getByLabel/getByPlaceholder/getByText用于用户可见文本- 基于稳定属性的 CSS
- XPath,仅作为最后手段,并附上原因注释
如果用户没有提供 DOM、HTML 或屏幕截图,你就是在猜测定位器。写出最佳猜测并标记为 // TODO-VERIFY 定位器,以免静默发布带有虚构选择器的代码。在摘要中提及未验证定位器的数量。
4. 编写测试
- 每个手动用例对应一个
@Test方法。方法名 = 行为(shouldShowErrorForLockedUser),而非 ID。 - 可追溯性:将用例 ID 放在
@TmsLink("TC-012")和description中。添加从用例优先级映射的@Epic、@Feature、@Story、@Severity(P0/严重 →BLOCKER/CRITICAL,P1 →NORMAL,P2+ →MINOR)。 - 每个预期结果都变成一个断言。没有断言的步骤意味着手动检查被丢弃,因此在完成之前进行交叉检查。
- 前提条件进入设置辅助方法或 API 调用,而不是长 UI 点击链,当存在 API 或深层链接时。对于登录前提条件(尤其是 OTP/SSO),首选一次 API 登录,并通过
context.storageState()/Browser.NewContextOptions().setStorageStatePath(...)重用它。 - 不同数据的重复用例 → 一个带有 TestNG
@DataProvider的测试。@TmsLink是静态的,因此将用例 ID 作为第一数据列传递,并在测试内部调用Allure.link(caseId, "tms", caseId)以保持每行可追溯性。将测试数据保留在测试体之外(数据提供程序、JSON 或常量类)。永远不要使用真实客户数据。 - 测试必须独立:不依赖执行顺序,每个测试使用新的上下文,清理所有创建的内容。
5. 等待:仅基于条件
这是 flakiness 的主要来源,因此将其视为硬性规则:
- 永远不要使用
Thread.sleep或page.waitForTimeout。 - 首选 web-first 断言:
assertThat(locator).isVisible()、hasText、hasCount、assertThat(page).hasURL(...)。它们会自动重试。 - 操作已经自动等待可操作性;不要在
click()/fill()之前添加多余的等待。 - 当操作触发一个其结果重要的网络调用时,将其包装:
page.waitForResponse(r -> r.url().contains("/cart") && r.ok(), () -> addButton.click())。 - 对于旋转器/加载器:
assertThat(loader).isHidden()。 - 对于导航:
page.waitForURL("**/checkout**")。 - 自定义超时来自配置,永远不要使用魔法数字内联。
参见 references/framework-scaffold.md → “等待模式”以获取代码片段。
6. 并行安全
所有 Playwright 对象(Playwright、Browser、BrowserContext、Page)都存在于由基类/工厂管理的 ThreadLocal 中,并在每个测试方法中创建和关闭。永远不要将它们存储在静态字段中或在测试之间共享 Page。页面对象通过构造函数接收 Page。不允许在页面对象中使用静态状态。
输出格式
按以下顺序回复:
- 文件:每个文件在其自己的代码块中,并带有完整路径作为标题(例如
src/test/java/com/example/pages/LoginPage.java)。完整的、可编译的文件,带有导入,而不是片段,除非用户要求代码片段。如果超过约 3 个文件或用户想要下载它们,也将其作为项目文件夹写入磁盘。 - 可追溯性表格:
用例 ID | 测试方法 | 已自动化步骤 | 备注。 - 未自动化/差距:跳过的用例或步骤及其原因。
- 需要验证:
TODO-VERIFY定位器的数量和列表,关于测试数据或环境的假设。
保持文字简洁;代码是交付物。
保密性
当用户正在构建公共作品集或请求提及公共演示应用程序时,保持所有内容通用:包 com.example(或他们指定的),无公司名称、内部 URL、内部端点、真实用户数据或专有事件名称。如果粘贴的手动用例包含这些内容,将其替换为中性等效项,并用一行说明。
参考文件
references/framework-scaffold.md:pom.xml 依赖、带有 ThreadLocal 的 PlaywrightFactory、带有跟踪和失败时 Allure 附件的 BaseTest、BasePage、ConfigReader、用于并行运行的 testng.xml、等待模式代码片段。在搭建框架或需要确切 API 形状时阅读它。references/example-saucedemo.md:一个完整的工作示例(手动用例 → 页面对象 → 测试 → 可追溯性表格),基于公共 saucedemo.com 应用程序。阅读它以校准输出风格。