拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Playwright Java 测试运行器接入指南:JUnit 与 TestNG 集成、类级并行执行及 Gradle 构建配置

Playwright Java 测试运行器接入指南JUnit 与 TestNG 集成、类级并行执行及 Gradle 构建配置【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright本文以 Playwright 官方 Java 文档 Test Runners 为主体讲解如何将 Playwright 接入 Java 生态中最常用的两个测试运行器 JUnit 与 TestNG包括 Playwright/Browser/BrowserContext/Page 四级对象的生命周期管理、JUnit 5 类级并行执行的完整配置、GradleGroovy/Kotlin DSL构建脚本接入方式以及 Playwright 命令行工具的 Gradle 任务封装。读完本文你可以直接复制可运行的配置到项目中并理解浏览器跨测试共享、上下文按测试隔离、Playwright 实例按线程独占这套并行化设计的底层原因。核心设计对象生命周期与测试隔离Playwright Java 接入测试运行器的核心原则是分级复用Playwright与Browser在整个测试类生命周期内共享一次创建、一次销毁避免反复启动浏览器进程的开销BrowserContext与Page为每个测试方法新建、测试结束后关闭保证各测试之间的浏览器状态Cookie、LocalStorage、缓存、弹窗等完全隔离。官方文档明确建议We recommend running each test case in a new BrowserContext, this way browser state will be isolated between the tests. 这与 Writing tests 中 Test Isolation 一节的说法一致BrowserContext 是一个内存中隔离的浏览器 profile每个测试创建独立上下文可确保测试互不干扰。理解这一点后后文 JUnit 与 TestNG 两个示例的差异就只是生命周期钩子注解不同生命周期层次JUnit 5 注解TestNG 注解类级启动Playwright BrowserBeforeAll/AfterAllBeforeClass/AfterClass方法级BrowserContext PageBeforeEach/AfterEachBeforeMethod/AfterMethod测试方法TestTestJUnit 5 接入标准写法静态生命周期钩子在 JUnit 中Playwright与Browser初始化为静态成员在BeforeAll中创建AfterAll中销毁BrowserContext与Page为非静态成员逐方法创建/销毁。以下示例完整来自官方文档三个测试方法共用同一个Browser每个测试使用自己的BrowserContext和Pagepackage org.example; import com.microsoft.playwright.Browser; import com.microsoft.playwright.BrowserContext; import com.microsoft.playwright.Page; import com.microsoft.playwright.Playwright; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class TestExample { // Shared between all tests in this class. static Playwright playwright; static Browser browser; // New instance for each test method. BrowserContext context; Page page; BeforeAll static void launchBrowser() { playwright Playwright.create(); browser playwright.chromium().launch(); } AfterAll static void closeBrowser() { playwright.close(); } BeforeEach void createContextAndPage() { context browser.newContext(); page context.newPage(); } AfterEach void closeContext() { context.close(); } Test void shouldClickButton() { page.navigate(data:text/html,scriptvar result;/scriptbutton onclickresult\Clicked\Go/button); page.locator(button).click(); assertEquals(Clicked, page.evaluate(result)); } Test void shouldCheckTheBox() { page.setContent(input idcheckbox typecheckbox/input); page.locator(input).check(); assertTrue((Boolean) page.evaluate(() window[checkbox].checked)); } Test void shouldSearchWiki() { page.navigate(https://www.wikipedia.org/); page.locator(input[name\search\]).click(); page.locator(input[name\search\]).fill(playwright); page.locator(input[name\search\]).press(Enter); assertEquals(https://en.wikipedia.org/wiki/Playwright, page.url()); } }实现要点说明Playwright.create()是 Java 驱动的入口内部启动 Playwright server 进程playwright.close()会连同其下的 Browser 一起回收AfterAll中无需单独调用browser.close()三个测试覆盖了三种典型自动化场景data:URL evaluate做点击验证、setContent做本地 DOM 交互验证、真实站点导航验证端到端流程如需有头模式调试可改为playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false))参见 Running and debugging tests。实验性 JUnit 集成UsePlaywright官方文档还链接了一个实验性experimental的 JUnit 集成仓库中对应 JUnit (experimental)。它通过UsePlaywright注解 JUnit fixture 机制自动完成初始化测试方法直接以Page page作为参数注入无需手写任何生命周期代码package org.example; import com.microsoft.playwright.Page; import com.microsoft.playwright.junit.UsePlaywright; import org.junit.jupiter.api.Test; import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; UsePlaywright public class TestExample { Test void shouldClickButton(Page page) { page.navigate(data:text/html,scriptvar result;/scriptbutton onclickresult\Clicked\Go/button); page.locator(button).click(); assertEquals(Clicked, page.evaluate(result)); } Test void shouldSearchWiki(Page page) { page.navigate(https://www.wikipedia.org/); page.locator(input[name\search\]).click(); page.locator(input[name\search\]).fill(playwright); page.locator(input[name\search\]).press(Enter); assertThat(page).hasURL(https://en.wikipedia.org/wiki/Playwright); } }该实验性集成预定义了五组 fixture官方给出的对照表如下Fixture类型说明pagePage本次测试运行独立的页面browserContextBrowserContext本次测试运行独立的上下文pagefixture 也隶属于该上下文browserBrowser浏览器在测试之间共享以优化资源playwrightPlaywrightPlaywright 实例在同线程运行的测试之间共享requestAPIRequestContext本次测试运行独立的 APIRequestContext用于 API 测试注意 fixture 表中playwright 实例在同线程之间共享这一条——这恰好呼应了下一节的并行化约束。并行运行测试为什么必须每线程一个 Playwright官方文档给出的并行化建议是类内测试顺序执行、多个测试类并行执行且每个线程创建自己的 Playwright 实例并只在该线程使用。这条建议的根源在 Multithreading 文档中有明确说明Playwright Java is not thread safe, i.e. all its methods as well as methods on all objects created by it (such as BrowserContext, Browser, Page etc.) are expected to be called on the same thread where the Playwright object was created.即 Playwright Java 及其创建的所有对象都不是线程安全的同一实例的多线程并发调用需要自行加锁同步官方推荐的并行模式是每线程独立 Playwright 实例、各自启动独立浏览器进程。因此并行方案的关键就是让每个测试类持有独立的 Playwright/Browser 副本。实现PER_CLASS 实例生命周期 公共基类JUnit 默认每个测试方法都会新建一个测试类实例无法承载类级共享的静态外字段。解决方式是使用TestInstance(TestInstance.Lifecycle.PER_CLASS)注解让 JUnit 对整个测试类只创建一个实例JUnit 5.4再把 Playwright/Browser 存为实例字段。官方示例的结构是公共基类TestFixtures承担全部生命周期管理业务测试类只继承并写断言// Subclasses will inherit PER_CLASS behavior. TestInstance(TestInstance.Lifecycle.PER_CLASS) class TestFixtures { // Shared between all tests in the class. Playwright playwright; Browser browser; BeforeAll void launchBrowser() { playwright Playwright.create(); browser playwright.chromium().launch(); } AfterAll void closeBrowser() { playwright.close(); } // New instance for each test method. BrowserContext context; Page page; BeforeEach void createContextAndPage() { context browser.newContext(); page context.newPage(); } AfterEach void closeContext() { context.close(); } } class Test1 extends TestFixtures { Test void shouldClickButton() { page.navigate(data:text/html,scriptvar result;/scriptbutton onclickresult\Clicked\Go/button); page.locator(button).click(); assertEquals(Clicked, page.evaluate(result)); } Test void shouldCheckTheBox() { page.setContent(input idcheckbox typecheckbox/input); page.locator(input).check(); assertTrue((Boolean) page.evaluate(() window[checkbox].checked)); } Test void shouldSearchWiki() { page.navigate(https://www.wikipedia.org/); page.locator(input[name\search\]).click(); page.locator(input[name\search\]).fill(playwright); page.locator(input[name\search\]).press(Enter); assertEquals(https://en.wikipedia.org/wiki/Playwright, page.url()); } } class Test2 extends TestFixtures { Test void shouldReturnInnerHTML() { page.setContent(divhello/div); assertEquals(hello, page.innerHTML(cssdiv)); } Test void shouldClickButton() { Page popup page.waitForPopup(() - { page.evaluate(window.open(about:blank);); }); assertEquals(about:blank, popup.url()); } }从源码结构看Test1、Test2各自继承TestFixtures后会继承PER_CLASS行为JUnit 为Test1创建一个实例对应一个 Playwright 一个浏览器进程为Test2再创建另一个实例另一个 Playwright 另一个浏览器进程。两个类被 JUnit 分配到不同线程并发执行时各自访问的 Playwright 对象从未跨线程共享天然满足线程安全约束。JUnit 并行配置项自 JUnit 5.3 起支持并行执行官方推荐类内顺序、类间并行、线程数 CPU 核数的一半的配置写入junit-platform.properties或对应构建配置junit.jupiter.execution.parallel.enabled true junit.jupiter.execution.parallel.mode.default same_thread junit.jupiter.execution.parallel.mode.classes.default concurrent junit.jupiter.execution.parallel.config.strategydynamic junit.jupiter.execution.parallel.config.dynamic.factor0.5各配置项含义parallel.enabled true总开关启用并行执行parallel.mode.default same_thread默认类内测试方法之间保持在同一线程顺序执行保证单个类内的测试不产生并发parallel.mode.classes.default concurrent测试类之间并行调度到不同线程parallel.config.strategy dynamic使用动态线程池策略parallel.config.dynamic.factor 0.5动态因子 0.5即最大并行线程数取 CPU 核数的一半。官方取 0.5 而非更激进的核数是为每个浏览器进程留出渲染与 I/O 余量如果你的测试更偏 CPU 密集大量截图比对、录制视频可以适当调大该因子观察机器负载。使用 Gradle 构建Playwright Java 依赖发布在 Maven Central模块坐标为com.microsoft.playwright:playwright。官方文档给出了 Groovy 与 Kotlin 两种 DSL 的完整构建脚本文档中的%%VERSION%%是文档构建时的版本占位符实际项目中替换为具体版本号即可。Groovybuild.gradleplugins { application id java } repositories { mavenCentral() } dependencies { implementation com.microsoft.playwright:playwright:%%VERSION%% } application { mainClass org.example.App } // Usage: ./gradlew playwright --argshelp task playwright(type: JavaExec) { classpath sourceSets.test.runtimeClasspath mainClass com.microsoft.playwright.CLI } test { useJUnitPlatform() }Kotlinbuild.gradle.ktsplugins { application id(java) } repositories { mavenCentral() } dependencies { implementation(com.microsoft.playwright:playwright:%%VERSION%%) } application { mainClass.set(org.example.App) } // Usage: ./gradlew playwright --argshelp tasks.registerJavaExec(playwright) { classpath(sourceSets[test].runtimeClasspath) mainClass.set(com.microsoft.playwright.CLI) } tasks.test { useJUnitPlatform() testLogging { events(passed, skipped, failed) } }两个脚本的要点application插件让普通 Java 主程序可以直接./gradlew run运行test { useJUnitPlatform() }声明使用 JUnit 5 平台执行测试关键在自定义的playwright任务它是一个JavaExec任务mainClass指向com.microsoft.playwright.CLI即 Playwright Java 驱动的命令行入口提供codegen、install、screenshot等子命令classpath 复用测试运行时依赖这样无需全局安装任何东西即可在构建内运行 Playwright CLI。常用命令# 以应用方式运行主程序 ./gradlew run # 运行测试 ./gradlew test # 调用 Playwright 命令行工具帮助 ./gradlew playwright --argshelpTestNG 接入TestNG 版本的接入结构与 JUnit 完全同构只是钩子注解不同Playwright与Browser为非静态实例字段在BeforeClass创建、AfterClass销毁BrowserContext与Page在BeforeMethod创建、AfterMethod关闭。完整示例如下package org.example; import com.microsoft.playwright.Browser; import com.microsoft.playwright.BrowserContext; import com.microsoft.playwright.Page; import com.microsoft.playwright.Playwright; import org.testng.annotations.*; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; public class TestExample { // Shared between all tests in this class. Playwright playwright; Browser browser; // New instance for each test method. BrowserContext context; Page page; BeforeClass void launchBrowser() { playwright Playwright.create(); browser playwright.chromium().launch(); } AfterClass void closeBrowser() { playwright.close(); } BeforeMethod void createContextAndPage() { context browser.newContext(); page context.newPage(); } AfterMethod void closeContext() { context.close(); } Test void shouldClickButton() { page.navigate(data:text/html,scriptvar result;/scriptbutton onclickresult\Clicked\Go/button); page.locator(button).click(); assertEquals(Clicked, page.evaluate(result)); } Test void shouldCheckTheBox() { page.setContent(input idcheckbox typecheckbox/input); page.locator(input).check(); assertTrue((Boolean) page.evaluate(() window[checkbox].checked)); } Test void shouldSearchWiki() { page.navigate(https://www.wikipedia.org/); page.locator(input[name\search\]).click(); page.locator(input[name\search\]).fill(playwright); page.locator(input[name\search\]).press(Enter); assertEquals(https://en.wikipedia.org/wiki/Playwright, page.url()); } }注意与 JUnit 静态版本的差异TestNG 示例中Playwright/Browser是实例字段TestNG 默认整个测试类共享一个实例而 JUnit 版本因默认每方法新建实例被迫使用static字段。两者的隔离效果完全一致——浏览器类级共享、上下文方法级隔离。与其他 Java 文档的衔接Playwright Java 支持任意测试框架官方在 Supported languages 中说明 Java 版本可自由选择 JUnit 或 TestNG。围绕本文主题的延伸阅读均在本仓库文档中Running and debugging tests单测/多测运行、有头模式setHeadless(false)、调试Writing testsWeb-first 断言assertThat、Locator 与 auto-wait 机制JUnit (experimental)UsePlaywrightfixture 化集成、OptionsFactory自定义 launch/context 选项Multithreading同步 API 的事件循环机制以及Page.waitForTimeout()与Thread.sleep()的行为差异——并行场景下尤其要注意Thread.sleep()期间浏览器事件不会被分发。小结将 Playwright 接入 Java 测试运行器的标准模式可以归纳为三条规则生命周期分层Playwright/Browser类级或线程级共享BrowserContext/Page方法级新建并关闭用测试框架的 before/after 钩子表达并行安全边界Playwright Java 非线程安全并行时让每个测试类或线程拥有独立 Playwright 实例配合 JUnit 5 的类间 concurrent、类内 same_thread配置实现安全提速构建集成Gradle 中引入 Maven Central 依赖并注册com.microsoft.playwright.CLI的JavaExec任务即可在构建内同时运行测试与 Playwright 命令行工具。【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门