Playwright for Java is Microsoft's official Java library for browser automation. It gives Java teams the same cross-browser testing power, auto-waiting, and tracing that made Playwright the #1 test framework in TypeScript — without leaving the Java ecosystem. If your team runs Selenium with Maven and JUnit, Playwright is a drop-in upgrade that eliminates driver management, explicit waits, and most flaky tests.
This tutorial assumes you know basic Java and Maven. No prior Playwright or Selenium experience needed.
Why Playwright + Java?
What you get with Playwright Java
- Cross-browser: Chromium, Firefox, WebKit
- Auto-waiting on every action (no WebDriverWait)
- No driver management (no ChromeDriver/GeckoDriver)
- Trace Viewer for visual debugging
- Network interception & mocking
- Screenshot & video on failure
- Works with JUnit 5 and TestNG
- Maven and Gradle support
Step 1: Maven Setup
Add the Playwright and JUnit 5 dependencies to your pom.xml:
<project> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <dependencies> <!-- Playwright --> <dependency> <groupId>com.microsoft.playwright</groupId> <artifactId>playwright</artifactId> <version>1.48.0</version> </dependency> <!-- JUnit 5 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.10.2</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.2.5</version> </plugin> </plugins> </build> </project>
Install browsers after adding the dependency:
# Download dependencies mvn install # Install browser binaries mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"
Java version: Playwright requires Java 8+, but Java 17 or 21 is recommended. Use java --version to check.
Step 2: Project Structure
my-playwright-java/ ├── src/ │ └── test/ │ └── java/ │ ├── tests/ │ │ ├── TestHomepage.java │ │ └── TestLogin.java │ ├── pages/ // Page Object Model │ │ └── LoginPage.java │ └── base/ │ └── BaseTest.java // Shared setup/teardown ├── pom.xml └── README.md
Step 3: Write Your First Test
Unlike Selenium, there's no driver setup, no System.setProperty, and no WebDriverManager. Playwright handles everything:
package tests; import com.microsoft.playwright.*; import org.junit.jupiter.api.*; import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; import java.util.regex.Pattern; public class TestHomepage { static Playwright playwright; static Browser browser; BrowserContext context; Page page; @BeforeAll static void setup() { playwright = Playwright.create(); browser = playwright.chromium().launch(); } @BeforeEach void createContext() { context = browser.newContext(); page = context.newPage(); } @AfterEach void closeContext() { context.close(); } @AfterAll static void teardown() { playwright.close(); } @Test void homepageHasTitle() { page.navigate("https://playwright.dev/"); assertThat(page).hasTitle(Pattern.compile("Playwright")); } @Test void getStartedLink() { 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(); } }
Run it:
# Run all tests mvn test # Run a specific test class mvn test -Dtest=TestHomepage # Run a specific method mvn test -Dtest=TestHomepage#homepageHasTitle
Step 4: Base Test Class (Clean Setup)
Extract browser lifecycle into a reusable base class:
package base; import com.microsoft.playwright.*; import org.junit.jupiter.api.*; public class BaseTest { static Playwright playwright; static Browser browser; protected BrowserContext context; protected Page page; @BeforeAll static void launchBrowser() { playwright = Playwright.create(); browser = playwright.chromium().launch( new BrowserType.LaunchOptions().setHeadless(true) ); } @BeforeEach void createContextAndPage() { context = browser.newContext( new Browser.NewContextOptions() .setViewportSize(1280, 720) ); page = context.newPage(); } @AfterEach void closeContext() { context.close(); } @AfterAll static void closeBrowser() { playwright.close(); } }
Now test classes simply extend BaseTest:
package tests; import base.BaseTest; import com.microsoft.playwright.*; import org.junit.jupiter.api.Test; import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; public class TestLogin extends BaseTest { @Test void successfulLogin() { page.navigate("http://localhost:3000/login"); page.getByLabel("Email").fill("user@example.com"); page.getByLabel("Password").fill("securePass123"); page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions() .setName("Sign In")).click(); assertThat(page).hasURL("http://localhost:3000/dashboard"); } @Test void invalidCredentialsShowError() { page.navigate("http://localhost:3000/login"); page.getByLabel("Email").fill("wrong@example.com"); page.getByLabel("Password").fill("wrongpass"); page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions() .setName("Sign In")).click(); assertThat(page.getByRole(AriaRole.ALERT)) .hasText("Invalid email or password"); } }
Step 5: Locator Strategies
// ✅ BEST: Role-based locators (accessible, resilient) page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Submit")); page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("Sign In")); page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setName("Dashboard")); // ✅ GOOD: Label and placeholder page.getByLabel("Email address"); page.getByPlaceholder("Search..."); // ✅ GOOD: Test ID page.getByTestId("checkout-button"); // ✅ OK: Text content page.getByText("Add to Cart"); // ⚠️ AVOID: CSS selectors (brittle, Selenium habits) page.locator("#submit-btn"); page.locator(".form-container > button.primary");
For Selenium migrants: Replace driver.findElement(By.cssSelector(...)) with page.getByRole() or page.getByLabel(). These role-based locators are more resilient to UI changes and align with accessibility standards.
Step 6: Page Object Model
package pages; import com.microsoft.playwright.*; import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; public class LoginPage { private final Page page; private final Locator emailField; private final Locator passwordField; private final Locator submitButton; private final Locator errorAlert; public LoginPage(Page page) { this.page = page; this.emailField = page.getByLabel("Email"); this.passwordField = page.getByLabel("Password"); this.submitButton = page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Sign In")); this.errorAlert = page.getByRole(AriaRole.ALERT); } public LoginPage navigate() { page.navigate("http://localhost:3000/login"); return this; } public void login(String email, String password) { emailField.fill(email); passwordField.fill(password); submitButton.click(); } public void expectError(String message) { assertThat(errorAlert).hasText(message); } }
@Test void successfulLogin() { LoginPage loginPage = new LoginPage(page).navigate(); loginPage.login("user@example.com", "securePass123"); assertThat(page).hasURL("http://localhost:3000/dashboard"); } @Test void invalidLogin() { LoginPage loginPage = new LoginPage(page).navigate(); loginPage.login("wrong@test.com", "wrong"); loginPage.expectError("Invalid email or password"); }
Step 7: Tracing & Debugging
Enable tracing to capture every action, DOM snapshot, and network request:
@BeforeEach void createContextAndPage() { context = browser.newContext(); // Start tracing before each test context.tracing().start(new Tracing.StartOptions() .setScreenshots(true) .setSnapshots(true) .setSources(true)); page = context.newPage(); } @AfterEach void closeContext(TestInfo testInfo) { // Save trace on failure context.tracing().stop(new Tracing.StopOptions() .setPath(Paths.get("traces/" + testInfo.getDisplayName() + ".zip"))); context.close(); }
View traces with:
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI \ -D exec.args="show-trace traces/successfulLogin.zip"
Step 8: Cross-Browser Testing
Switch browsers by changing the launcher in BaseTest:
// Chromium (default) browser = playwright.chromium().launch(); // Firefox browser = playwright.firefox().launch(); // WebKit (Safari engine) browser = playwright.webkit().launch(); // Headed mode (visible browser window) browser = playwright.chromium().launch( new BrowserType.LaunchOptions().setHeadless(false).setSlowMo(500) );
JUnit 5 parameterized tests: Use @ParameterizedTest with @ValueSource(strings = {"chromium", "firefox", "webkit"}) to run the same test across all browsers automatically.
Step 9: CI/CD with GitHub Actions
name: Playwright Java Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: distribution: "temurin" java-version: "17" - run: mvn install -DskipTests - run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps chromium" - run: mvn test - uses: actions/upload-artifact@v4 if: always() with: name: playwright-traces path: traces/
Playwright Java vs Selenium Java
If you're deciding between the two for a Java project:
- Auto-waiting: Playwright auto-waits on every action. Selenium requires
WebDriverWait+ExpectedConditionseverywhere. - Driver management: Playwright bundles browsers — no ChromeDriver, GeckoDriver, or WebDriverManager needed.
- Speed: Playwright uses direct browser protocols. Selenium goes through WebDriver HTTP bridge.
- Debugging: Playwright has Trace Viewer with DOM snapshots. Selenium has manual logging and screenshots.
- Locators: Playwright's
getByRole()andgetByLabel()are more resilient thanBy.cssSelector(). - AI integration: Playwright has MCP Server for Claude AI test generation. Selenium has no equivalent.
For the full comparison, see Playwright vs Cypress vs Selenium 2026.
Frequently Asked Questions
Can I use Playwright with Java?
Yes. Playwright has an official Java library maintained by Microsoft. Add it as a Maven or Gradle dependency and use it with JUnit 5 or TestNG. Same features as the TypeScript version.
Is Playwright Java better than Selenium Java?
For new projects, yes. It's faster, has auto-waiting, bundles browsers (no driver management), and includes Trace Viewer. Selenium is only better for IE11 or Ruby/PHP orgs.
What Java version does Playwright require?
Java 8+. Java 17 or 21 is recommended for modern language features.
Should I use JUnit 5 or TestNG?
JUnit 5 for new projects — it has better extension support, parameterized tests, and cleaner lifecycle hooks. TestNG is fine if your team already uses it.
How do I migrate from Selenium Java to Playwright Java?
Run both side by side. Add Playwright as a Maven dependency, write new tests with Playwright. Replace WebDriverWait with auto-waiting, findElement(By.cssSelector()) with getByRole()/getByLabel(), and remove driver setup entirely.
Asim Noaman
Senior QA Automation Engineer & AI Testing Specialist
With years of hands-on experience building test automation frameworks for production applications, Asim specializes in combining traditional QA methodologies with cutting-edge AI tools. He has helped teams adopt Playwright and AI-driven testing workflows to ship faster with fewer bugs.