目录
[1. 框架定位](#1. 框架定位)
[2. 工程结构](#2. 工程结构)
[3. 浏览器生命周期](#3. 浏览器生命周期)
[4. 配置逻辑](#4. 配置逻辑)
[5. 登录态:为什么不用 UI 登录](#5. 登录态:为什么不用 UI 登录)
[6. 样例用例执行逻辑(CreateXXAccountTest)](#6. 样例用例执行逻辑(CreateXXAccountTest))
[6.1 执行时序](#6.1 执行时序)
[6.2 页面操作明细](#6.2 页面操作明细)
[6.3 和 Page Object 示例的差异](#6.3 和 Page Object 示例的差异)
[7. 失败处理与可观测性](#7. 失败处理与可观测性)
[8. 如何运行这条用例](#8. 如何运行这条用例)
[9. 再写一条业务用例的推荐步骤](#9. 再写一条业务用例的推荐步骤)
[10. 当前局限](#10. 当前局限)
一、环境搭建:
框架:playwright+testng+java+Allure
环境搭建与配置:
安装:idea、jdk21、maven3.x
配置环境变量:
|--------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|
| 工具 | 下载与安装 | 系统环境变量配置 | 其他设置 | |
| idea | 官网下载即可 | 无 | 配置jdk:以下几个位置设置jdk版本
配置maven:file->settings
| |
| jdk21 | | 
java -version 探测是否配置成功,显示版本号为配置成功 | | |
| maven3 | |
mvn -v探测是否配置成功,显示版本号为配置成功 | | |
二、框架搭建
1. 框架定位
这是一套 Maven + JDK 17 + TestNG + Playwright Java 的 Web UI 自动化框架。
设计目标:
-
浏览器生命周期统一管理,用例之间状态隔离
-
配置与代码分离,本地 / CI 可用不同环境
-
业务用例只写页面操作和断言,不自己启动浏览器
-
失败可追溯:日志、截图、Playwright Trace、失败重试
-
需要登录的业务页,用
auth.json(storageState)复用登录态,避开验证码
核心依赖:Playwright 1.55.0、TestNG 7.10.2、AssertJ、Allure TestNG、SLF4J + Log4j2。
2. 工程结构
playwright_java_demo
├── pom.xml
├── auth.json # 登录态(cookie + localStorage),由 GenAuthManual 生成
├── src/main/java/com/playauto
│ ├── config/EnvConfig.java # 读取 config*.properties,支持 -D 覆盖
│ ├── driver/PlaywrightManager.java # Playwright / Browser / Context / Page
│ ├── pages/ # Page Object(BasePage + 业务页)
│ ├── common/ScreenshotHelper.java # 失败截图
│ └── listener/ # 日志 / 截图 / 失败重试
├── src/main/resources
│ ├── config.properties # 默认环境(当前指向 xx服务 测试环境)
│ ├── config-dev.properties
│ ├── config-test.properties
│ └── log4j2.xml
└── src/test/java/com/playauto
├── base/BaseTest.java # 用例基类
└── cases
├── yewucase_package/ # 业务用例(xxxx)
├── demo/ # 示例用例(百度、TodoMVC)
└── smoke/ # 冒烟
分层约定:
|-----|-----------------------------|-------------------------------------|
| 层 | 类 | 职责 |
| 配置层 | EnvConfig | 浏览器、超时、headless、base.url |
| 驱动层 | PlaywrightManager | 启动/关闭浏览器,创建 Context,注入 storageState |
| 基类层 | BaseTest | TestNG 生命周期,暴露 page |
| 页面层 | BasePage 及子类 | 页面操作封装(POM) |
| 用例层 | CreateAgencyAccountTest 等 | 编排步骤,不做驱动管理 |
| 监听层 | TestNG Listener | 日志、失败截图、失败重试 |
3. 浏览器生命周期
官方 Playwright + TestNG 模型:
-
每个测试类启动一次 Browser(
@BeforeClass) -
每个
@Test方法新建独立的BrowserContext+Page(@BeforeMethod) -
方法结束关闭 Context(
@AfterMethod) -
类结束关闭 Browser(
@AfterClass)
这样每条用例的 cookie、localStorage、登录态互不影响。Playwright 对象非线程安全,套件并行只能 parallel="classes",不能 parallel="methods"。
PlaywrightManager 用 ThreadLocal 保存 Playwright / Browser / Context / Page,避免跨线程串用。
创建 Context 时还会:
-
设置视口、忽略 HTTPS 错误、语言
zh-CN、时区Asia/Shanghai -
可选加载
auth.json作为storageState -
注入脚本隐藏
navigator.webdriver -
按配置开启 Trace / 录像
-
设置默认动作超时、导航超时
4. 配置逻辑
EnvConfig 启动时加载:
-
-Denv=dev|test→config-dev.properties/config-test.properties -
未指定 →
config.properties -
任意 key 都可用 JVM 参数覆盖,例如
-Dheadless=false
当前默认配置要点:
-
base.url:https://域名url/#/login -
browser=chrome(走本机 Chrome channel) -
headless=false -
timeout.ms=15000,navigation.timeout.ms=30000 -
retry.count=1 -
失败截图、失败 Trace 开启
5. 登录态:为什么不用 UI 登录
XXLoginTest 走登录页时需要填图片验证码,自动化不稳定。框架采用 Playwright 官方 storageState 方案:
-
运行
com.playauto.cases.xx.GenAuthManual -
弹出浏览器,人工完成登录(含验证码)
-
控制台回车后,把 cookie + localStorage 写到项目根目录
auth.json -
业务用例在创建 Context 时加载该文件,打开业务页时已是登录态
BaseTest 提供钩子,子类按需覆盖:
protected Path storageStatePath() {
return null; // 默认不加载登录态
}
CreateXXAccountTest 覆盖为:
@Override
protected Path storageStatePath() {
return Paths.get("auth.json");
}
加载时机在 page.navigate() 之前。如果文件不存在,直接失败并提示先跑 GenAuthManual。token 过期后需重新生成 auth.json。
注意:auth.json 含登录凭证,不要提交到 Git。
6. 样例用例执行逻辑(CreateXXAccountTest)
目标:在代理商账户页新建一个代理商账户。
页面:https://域名/#/页面url
6.1 执行时序
TestNG 启动 CreateXXAccountTest
→ @BeforeClass launchBrowser()
Playwright 启动 Chrome(有头)
→ @BeforeMethod createContextAndPage()
读取 storageStatePath() = auth.json
用 cookie/localStorage 创建 BrowserContext
新建 Page,赋给 this.page
→ @Test shouldCreateXXAccount()
-
navigate 到XX账户页(此时已登录)
-
点「新建代理商账户」
-
填xx信息
-
选「返佣模式」
-
填登录用户信息
-
点「确定」
→ 失败则 FailureArtifactListener 截图
PlaywrightManager 保存 Trace
→ @AfterMethod 关闭 Context
→ @AfterClass 关闭 Browser
6.2 页面操作明细
定位策略以 ARIA Role + 可见名称 为主(getByRole / getByText),避免脆弱 CSS。
|----|----------------------------|------------|
| 步骤 | 操作 | 数据 |
| 1 | 打开xx账户页 | 见 PAGE_URL |
| 2 | 点击按钮「新建xx账户」 | --- |
| 3 | 填写「xx名称*」 | xxx |
| 4 | 填写「账户联系人*」 | 自动创建 |
| 5 | 填写「联系人电话*」 | xxxx |
| 6 | 填写「联系人邮箱*」 | xxxx |
| 7 | 点击「xx模式」 | --- |
| 8 | 填写「用户名称*」 | xx |
| 9 | 填写「邮箱*」(exact,避免和联系人邮箱混淆) | xx |
| 10 | 填写「登录密码*」 | xx |
| 11 | 填写「确认登录密码*」 | xx |
| 12 | 点击按钮「确定」 | --- |
「邮箱*」使用 setExact(true),是因为页面上同时存在「联系人邮箱*」和「邮箱*」,精确匹配才能点到登录邮箱。
6.3 和 Page Object 示例的差异
TodoMvcTest 走标准 POM:页面操作在 TodoMvcPage,用例只编排 + 断言。
CreateXXAccountTest 目前把录制步骤写在测试类里,便于快速落地业务流。后续建议抽成 XXAccountPage,用例里只保留:
-
打开页面
-
调用
createXX(...) -
断言创建成功(toast / 列表出现新账户)
当前用例还没有断言,属于流程冒烟;要做成稳定回归,需要补成功提示或列表校验。
7. 失败处理与可观测性
TestNG 监听器在 src/test/resources/suites/testng.xml 中注册:
|---------------------------|---------------------------------------------------|
| 监听器 | 作用 |
| TestLogListener | 套件/用例开始、通过、失败、跳过日志 |
| FailureArtifactListener | @Test 失败后、Context 关闭前截图到 target/screenshots |
| RetryTransformer | 给所有 @Test 挂上 RetryAnalyzer,按 retry.count 重试 |
失败且 trace.on.failure=true 时,Trace 存到 target/traces/{类名.方法名}.zip。查看:
mvn exec:java -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="show-trace target/traces/CreateXXAgencyAccountTest.shouldCreateAgencyAccount.zip"
Allure 结果目录:target/allure-results。
8. 如何运行这条用例
先保证根目录有未过期的 auth.json。没有则先运行 GenAuthManual。
单独跑新建代理商:
mvn test -Dtest=com.playauto.cases.xx服务简称.CreateXXAccountTest
IDEA:打开工程 → 选 JDK 17+ → 在 CreateXXAccountTest 上右键 Run。
当前 testng.xml 还没挂上 xx服务 用例,全量 mvn clean test 不会跑到它。要进套件,在 xml 里加:
<class name="com.playauto.cases.xx服务简称.CreateXXAccountTest"/>
9. 再写一条业务用例的推荐步骤
-
若页面需登录:确认
auth.json有效,或重新跑GenAuthManual -
新建测试类,继承
BaseTest -
覆盖
storageStatePath()指向auth.json -
@Test中page.navigate(业务URL),再用getByRole操作 -
稳定后把操作抽到
pages下的 Page Object,用例只留编排和断言 -
需要进日常回归时,把类名加入对应 TestNG suite
10. 当前局限
-
登录依赖人工生成的
auth.json,过期后要重录 -
CreateXXAccountTest无结果断言,重复执行可能因账户名/邮箱已存在而失败 -
测试数据写死在代码里,后续可抽到 properties / 数据驱动
-
类名不要再用
Test,会和 TestNG 的@Test注解冲突(本用例已改为CreateXXAccountTest
三、实战-怎么快速写一个用例
前置录制脚本信息:
页面测试case脚本快速录制:
codegen 录制!先手动在浏览器点一遍业务,直接生成 locator 代码,省去手写定位器,写用例速度最快。
打开 cmd 执行录制命令(需要在项目根目录下执行--根目录下有pom.xml文件),直接录操作,复制生成的代码到用例里面改。
直接执行会报以下错误:mvn exec:java -Dexec.mainClass="com.microsoft.playwright.CLI" -Dexec.args="codegen 项目xiangmuxiang根目录"
PowerShell 对 maven 的 -Dxxx=value 参数解析异常,当 value 带空格时,-D 被吞掉,maven 收到参数变成 .args="codegen https://xxx",于是 maven 误以为这是插件前缀,报 No plugin found for prefix '.args=codegen https'。
解决:PowerShell 用双引号包裹整个‑D 参数(前面加反引号 )
mvn exec:java "-Dexec.mainClass=`"com.microsoft.playwright.CLI`"" "-Dexec.args=`"codegen https://测试域名/#/login`""
如果录制的时候想用本机 Chrome(不是 playwright 自带浏览器)
录制命令追加参数 --executable-path 指定本机 chrome 路径
mvn exec:java "-Dexec.mainClass=`"com.microsoft.playwright.CLI`"" "-Dexec.args=`"codegen --executable-path='C:\Users\EDY\AppData\Local\Google\Chrome\Application\chrome.exe' https://测试域名/#/login`""
重点坑:codegen 浏览器窗口大小≠视口 (viewport)!就算你手动拉大浏览器窗口,页面布局依旧受内部 viewport 控制;网页是响应式,视口小页面就切到移动端布局,菜单 / 侧边栏直接消失,内容展示不全,录制出来的 locator 全部不对Playw...。
✅解决:启动 codegen 时显式设置大视口(Java mvn 命令)
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen --viewport-size='1920,1080' --target=java"
命令行各种坑,执行以下脚本录制
src/test/java/com/playauto/cases/Record.java URL替换成要测试的页面链接
执行以下脚本录制
import com.microsoft.playwright.*;
//手动录制测试脚本
public class Record {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
BrowserType.LaunchOptions launchOptions = new BrowserType.LaunchOptions();
launchOptions.setArgs(java.util.List.of("--disable-gpu"));
launchOptions.setHeadless(false);
Browser browser = playwright.chromium().launch(launchOptions);
BrowserContext context = browser.newContext(
new Browser.NewContextOptions().setViewportSize(null)
// .setViewportSize(1920, 728)//关闭页面大小设置,页面元素会展示不全
);
Page page = context.newPage();
// page.navigate("要测试的页面url地址");
page.navigate("要测试的页面url地址");
// ↓暂停,此时自动弹出Playwright‑Inspector拾取窗口!
page.pause();
}
}
}
直接去要测试的页面上正常操作,操作完保存录制的脚本,将录制的内容只复制业务代码
将录制好的代码直接复制到用例里使用
执行方式:
指定浏览器执行:
多浏览器执行: