Java 版入门与差异
本教程共 59 篇 · 第 59 篇 · 更新于 2026-08-04 · 约 10 分钟阅读
本节目标:在 Maven 或 Gradle 项目里装好 Playwright,用 JUnit 5 跑通第一个测试。
Java 版是四种绑定里最「传统」的一个:只有同步 API,没有 await。
写起来一行是一行,像普通 Java 代码,反倒是最好上手的。
加依赖
Maven 项目在 pom.xml 里加:
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.61.0</version>
</dependency>
Gradle 项目在 build.gradle 里加:
dependencies {
implementation 'com.microsoft.playwright:playwright:1.61.0'
}
NoteJava 版的发布节奏比 Node.js 版慢半拍,版本号也不一定跟主线对齐。撰写本书时 Maven Central 上
com.microsoft.playwright:playwright的最新版本是 1.61.0,1.62.x 还没发布,所以上面写的是 1.61.0。你动手时先去 Maven Central 搜一下这个坐标,用当时的最新可用版本。
Java 要求 JDK 11 或以上。用 JUnit 5 集成的话,还要额外引入 junit-jupiter 测试依赖。
装浏览器
Java 版没有独立的 playwright 命令,得通过构建工具调用内置 CLI。
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"
这条命令有点长,建议存成脚本。它做的事跟其他语言的 playwright install 一样:下载三套浏览器二进制。
同一个 CLI 也能录制脚本:
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="codegen playwright.dev"
生成的代码就是 Java 写法,可以直接粘进测试类改。
第一个脚本
先看不带测试框架的裸写法,理解生命周期。
import com.microsoft.playwright.*;
public class App {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().launch();
Page page = browser.newPage();
page.navigate("https://playwright.dev");
System.out.println(page.title());
}
}
}
三个细节值得说:
Playwright.create()启动驱动进程,用 try-with-resources 包住,退出时自动关闭。- 打开网址的方法叫
navigate,不叫goto。因为goto是 Java 保留字,不能当方法名。 - 全程没有
await。每个调用都是阻塞的,返回时动作已经做完。
想看见浏览器界面,启动时传选项:
Browser browser = playwright.chromium().launch(
new BrowserType.LaunchOptions().setHeadless(false).setSlowMo(500));
JUnit 5 集成
真正写测试时,别自己管 Playwright 和 Browser。官方提供了 @UsePlaywright 注解,帮你全包了。
import com.microsoft.playwright.Page;
import com.microsoft.playwright.junit.UsePlaywright;
import com.microsoft.playwright.options.AriaRole;
import org.junit.jupiter.api.Test;
import java.util.regex.Pattern;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
@UsePlaywright
public class ExampleTest {
@Test
void hasTitle(Page page) {
page.navigate("https://playwright.dev");
assertThat(page).hasTitle(Pattern.compile("Playwright"));
}
@Test
void getStartedLink(Page page) {
page.navigate("https://playwright.dev");
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("Get started")).click();
assertThat(page.getByRole(AriaRole.HEADING,
new Page.GetByRoleOptions().setName("Installation"))).isVisible();
}
}
关键在于 Page page 这个方法参数。类上标了 @UsePlaywright,JUnit 就会自动注入。
每个测试方法拿到全新的 Page 和浏览器上下文,彼此隔离。浏览器实例在多个测试间复用,省启动开销。
能注入的类型有这些:
| 参数类型 | 作用 |
|---|---|
Page | 页面,最常用 |
BrowserContext | 上下文,需要开多页面时用 |
Browser | 浏览器实例 |
Playwright | 驱动入口 |
APIRequestContext | 做接口测试用 |
需要哪个就往参数里写哪个,不需要的不写。
Tip
@UsePlaywright也能标在测试类的父类上。团队里抽一个BaseTest,所有测试类继承它,注解只写一次。
定制启动选项
想改无头模式、浏览器通道、基础地址,写一个 OptionsFactory 实现类。
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.junit.Options;
import com.microsoft.playwright.junit.OptionsFactory;
public class MyOptions implements OptionsFactory {
@Override
public Options getOptions() {
return new Options()
.setHeadless(false)
.setChannel("chrome")
.setContextOptions(new Browser.NewContextOptions()
.setBaseURL("https://playwright.dev")
.setViewportSize(1440, 900));
}
}
然后把类名传给注解:
@UsePlaywright(MyOptions.class)
public class ExampleTest {
// ...
}
也可以在这里开 trace 记录:.setTrace(Options.Trace.RETAIN_ON_FAILURE)。失败时才留追踪文件,磁盘不容易爆。
断言写法
Java 版的 Web 优先断言在 PlaywrightAssertions 类里,静态导入后用 assertThat。
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
assertThat(page.getByTestId("status")).hasText("成功");
assertThat(page.getByRole(AriaRole.BUTTON)).isEnabled();
assertThat(page).hasURL(Pattern.compile(".*dashboard"));
名字虽然跟 AssertJ 的 assertThat 撞了,性质完全不同:这套断言会自动重试,等到条件成立或超时。
别用 JUnit 的 Assertions.assertEquals 判断页面状态。它只看一瞬间,页面没渲染完就红了。
想改单条断言的超时:
assertThat(locator).isVisible(new LocatorAssertions.IsVisibleOptions().setTimeout(10000));
命名与传参差异
Java 版的差异集中在三点。
方法名驼峰命名,跟 TS 版基本一致:getByRole、allTextContents、waitFor。
选项用链式 setter,不是对象字面量:
// TS 写法:page.goto(url, { waitUntil: 'networkidle' })
page.navigate("https://playwright.dev",
new Page.NavigateOptions().setWaitUntil(WaitUntilState.NETWORKIDLE));
// TS 写法:page.getByRole('button', { name: '登录' })
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("登录"));
选项类是内部类,名字规律是「所属类 + 方法名 + Options」。写 Page.NavigateOptions、Locator.ClickOptions,IDE 会自动补全。
角色用大写枚举:AriaRole.BUTTON、AriaRole.HEADING、AriaRole.LINK。这跟 C# 的 AriaRole.Button 只差大小写。
线程安全与并行
这条是 Java 版特有的坑,务必记住。
WarningPlaywright 的对象不是线程安全的。同一个
Page不能被多个线程同时操作。
正确做法是每个线程创建自己的 Playwright 实例。用 @UsePlaywright 的话,这件事框架已经帮你处理了,直接开并行就行。
JUnit 5 开并行,在 src/test/resources/junit-platform.properties 里配:
junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default = concurrent
junit.jupiter.execution.parallel.config.strategy = fixed
junit.jupiter.execution.parallel.config.fixed.parallelism = 4
并发度按 CPU 核数定。开太大只会互相抢资源。
TestNG 也能用
官方对 Java 不锁死测试框架,TestNG 一样跑得起来。只是没有 @UsePlaywright 这种现成注解。
用 TestNG 的话,自己写 @BeforeMethod / @AfterMethod 管理 Playwright 和 Page 的创建销毁。代码多几行,逻辑不复杂。
没有历史包袱就用 JUnit 5。官方注解省事,出问题也好查资料。
新手常踩的两个坑
第一个:自己 new Playwright 又忘了关。 不用 @UsePlaywright 时,Playwright.create() 会拉起一个 Node 驱动进程。忘了关,进程就一直挂着,跑几十个测试后机器内存吃满。
解法是永远用 try-with-resources 包住:
try (Playwright playwright = Playwright.create()) {
// 用完自动关闭驱动进程
}
第二个:把 Page 存成静态字段共享。 看起来能省创建开销,实际会让测试互相污染,并行时还会因为线程不安全直接崩。
每个测试拿自己的 Page,这是底线。@UsePlaywright 默认就是这个行为,别自己改回去。
跟 TS 版的对照速查
拿 TS 文档翻译成 Java,照这张表改。
| TypeScript | Java |
|---|---|
page.goto(url) | page.navigate(url) |
page.getByText('登录') | page.getByText("登录") |
locator.click() | locator.click() |
expect(l).toHaveText('x') | assertThat(l).hasText("x") |
{ timeout: 5000 } | new Locator.ClickOptions().setTimeout(5000) |
test.beforeEach | @BeforeEach |
断言方法名去掉了 to 前缀:toHaveText 变 hasText,toBeVisible 变 isVisible。这是 Java 断言库的惯例。
小结
Maven 加 com.microsoft.playwright:playwright,版本请以当时 Maven Central 最新可用版本为准(撰写本书时最新为 1.61.0)。
浏览器靠 mvn exec:java 调 CLI 安装,命令长,存成脚本。
Java 只有同步 API,没有 await;打开网址的方法叫 navigate。
JUnit 5 用 @UsePlaywright 注解,把 Page 写进测试方法参数就能自动注入。
断言用 PlaywrightAssertions.assertThat,会自动重试,别混用 JUnit 原生断言判断页面。
Playwright 对象不是线程安全的,并行时确保每个线程独立持有实例。