Java August 14, 2026 16 min read

Playwright Java Tutorial 2026: Maven, JUnit 5 & Your First Test

A complete guide to using Playwright with Java. From Maven pom.xml setup to JUnit 5 integration, role-based locators, Page Object Model, Trace Viewer debugging, cross-browser testing, and CI/CD — everything a Java developer needs to switch from Selenium to Playwright.

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:

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:

Terminal
# 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

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:

tests/TestHomepage.java
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:

Terminal
# 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:

base/BaseTest.java
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:

tests/TestLogin.java — using 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

Java — locator strategies (best to worst)
// ✅ 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

pages/LoginPage.java
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);
    }
}
tests/TestLogin.java — using POM
@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:

Java — tracing in BaseTest
@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:

Terminal
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:

Java — cross-browser
// 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

.github/workflows/playwright.yml
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 + ExpectedConditions everywhere.
  • 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() and getByLabel() are more resilient than By.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 - Playwright and Claude AI course instructor

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.

Udemy InstructorPublished course author
Playwright + AI ExpertSpecialized in AI-powered QA
Production ExperienceEnterprise-grade frameworks
Connect on LinkedIn