Playwright Automation Testing with Java
Set up Playwright with Java, write reliable browser tests, run them in JUnit or TestNG, and troubleshoot CI failures.

Direct answer: Playwright automation testing with Java uses the Playwright Maven dependency, a Playwright-managed browser, a BrowserContext for isolated test state, and locators plus retrying assertions. The workflow is: add the dependency, install matching browser binaries, launch a browser, navigate to your application, interact through locators, assert outcomes, and close resources. Playwright Java supports Chromium, Firefox, and WebKit and can run headed or headless in local development and CI. The official Java introduction currently shows dependency version 1.63.0; treat that as the version displayed in the documentation rather than a permanent recommendation.
What you need
- Java 8 or later, subject to the requirements for the Playwright version you choose.
- Maven or another build tool that can resolve Maven artifacts.
- A test runner such as JUnit or TestNG.
- Browser binaries installed for the same Playwright release as your dependency.
Check the current Playwright Java installation guide for supported operating systems and version-specific requirements.
1. Create a Maven project
Add Playwright and your chosen test runner to pom.xml. This example uses the dependency version shown in the retrieved Playwright documentation.
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<playwright.version>1.63.0</playwright.version>
<junit.version>5.11.0</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>${playwright.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.0</version>
</plugin>
</plugins>
</build>
Use the Playwright version that your project has selected after checking the current official documentation. Keep the browser installation step tied to that exact version.
2. Install browser binaries
After Maven has resolved the dependency, install the browsers and operating-system dependencies through the Java CLI:

mvn exec:java -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="install --with-deps"
On a machine where operating-system packages are managed separately, install only the browser binaries:
mvn exec:java -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="install"
For headless-only Chromium CI environments, the browser guide documents the --only-shell option:
mvn exec:java -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="install --only-shell"
Browser binaries track Playwright releases. If you upgrade the Maven dependency, rerun the install command. Playwright can also install branded Chrome or Edge, but those installations use the operating system’s default global location and can override an existing installation; use that option deliberately.
3. Write a first Java test
This JUnit 5 test creates one Playwright instance and browser for the test class, then creates a fresh context and page for each test. A context isolates cookies, local storage, permissions, and other browser state.
package example;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
class HomePageTest {
static Playwright playwright;
static Browser browser;
BrowserContext context;
Page page;
@BeforeAll
static void launchBrowser() {
playwright = Playwright.create();
browser = playwright.chromium().launch(
new BrowserType.LaunchOptions().setHeadless(true));
}
@BeforeEach
void createIsolatedContext() {
context = browser.newContext();
page = context.newPage();
}
@AfterEach
void closeContext() {
context.close();
}
@AfterAll
static void closeBrowser() {
browser.close();
playwright.close();
}
@Test
void pageHasExpectedHeading() {
page.navigate("https://example.com");
assertThat(page).hasTitle("Example Domain");
assertThat(page.locator("h1")).hasText("Example Domain");
}
}
Run it with:
mvn test
For local debugging, use headed mode and slow actions:
browser = playwright.chromium().launch(
new BrowserType.LaunchOptions()
.setHeadless(false)
.setSlowMo(250));
4. Use locators instead of brittle selectors
Locators express how a user finds an element and let Playwright wait for actionability. Prefer role, label, text, and test-id locators, then use CSS or XPath only when they are the clearest stable contract.
page.getByRole(AriaRole.BUTTON,
new Page.GetByRoleOptions().setName("Sign in")).click();
page.getByLabel("Email").fill("qa@example.com");
page.getByLabel("Password").fill("correct horse battery staple");
page.getByTestId("submit-login").click();
assertThat(page.getByRole(AriaRole.HEADING,
new Page.GetByRoleOptions().setName("Dashboard"))).isVisible();
Actions auto-wait for the target to become actionable. Assertions retry until the expected condition is met or the assertion timeout expires. This avoids fixed sleeps for ordinary UI synchronization.
5. Test navigation, forms, and network-dependent UI
@Test
void submitsSearch() {
page.navigate("https://your-app.example/search");
page.getByRole(AriaRole.TEXTBOX,
new Page.GetByRoleOptions().setName("Search")).fill("playwright");
page.getByRole(AriaRole.BUTTON,
new Page.GetByRoleOptions().setName("Search")).click();
assertThat(page.getByRole(AriaRole.LIST,
new Page.GetByRoleOptions().setName("Results"))).isVisible();
}
@Test
void waitsForAnApiResponse() {
page.navigate("https://your-app.example");
Response response = page.waitForResponse(
r -> r.url().contains("/api/products") && r.status() == 200,
() -> page.getByRole(AriaRole.BUTTON,
new Page.GetByRoleOptions().setName("Load products")).click());
assertThat(page.getByText("Products loaded")).isVisible();
}
When a test depends on a specific response, start waiting before the action that triggers it. Use an explicit selector wait, a delay, or network-idle waiting only when the application genuinely requires it.
6. Choose a browser and context configuration
| Need | Configuration |
|---|---|
| Chromium, Firefox, or WebKit coverage | Launch playwright.chromium(), playwright.firefox(), or playwright.webkit(). |
| Mobile-like viewport | Create a context with viewport, device scale factor, user agent, and touch settings appropriate to the scenario. |
| Authentication reuse | Save and load storage state, while keeping test data isolated. |
| Locale or timezone behavior | Set locale and timezone on the context. |
| Permissions | Grant only the permissions needed by the test context. |
| Downloads, uploads, popups | Register the corresponding event wait before triggering the action. |
BrowserContext context = browser.newContext(
new Browser.NewContextOptions()
.setViewportSize(1440, 900)
.setLocale("en-US")
.setTimezoneId("America/New_York"));
Use a separate context per test. Reusing a browser process and Playwright instance can reduce startup overhead, while context isolation prevents one test’s cookies or local storage from affecting another.
7. JUnit and TestNG integration
Playwright’s Java documentation covers both JUnit and TestNG. Select the runner that matches your existing build lifecycle, fixtures, reporting, and parallel execution model. The resource pattern remains the same: reuse Playwright and, where useful, a browser; create a context and page for each test; close the context after each test.
JUnit lifecycle checklist
- Create Playwright and the browser in a class or suite setup.
- Create a new context and page in per-test setup.
- Close the context in per-test teardown.
- Close browser and Playwright in suite teardown.
TestNG considerations
Map the same lifecycle to @BeforeSuite, @BeforeMethod, @AfterMethod, and @AfterSuite as appropriate. If tests run in parallel, never share a mutable context or page between parallel methods. Confirm that your runner’s parallel mode and any shared test data are safe.
8. Generate a starting test with Codegen
Codegen records browser interactions and generates test code. The official guide says it prioritizes role, text, and test-id locators.
mvn exec:java -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen https://your-app.example"
Generated code is a starting point. Replace accidental selectors, add assertions that describe the behavior you actually require, remove exploratory clicks, and parameterize test data before committing it.
9. Capture screenshots, videos, and diagnostics
page.screenshot(new Page.ScreenshotOptions()
.setPath(Paths.get("target/failure.png"))
.setFullPage(true));
Capture diagnostics in teardown when a test fails. Keep artifacts associated with the test name and build identifier so parallel runs do not overwrite one another. The Playwright documentation points to traces as a next debugging step; follow the current Java documentation for the exact trace configuration supported by your selected version.

10. CI execution
- Use a clean Java environment and resolve the same Maven lock or dependency versions on every run.
- Install Playwright browsers after dependency resolution; include operating-system dependencies when the runner image does not provide them.
- Run headless by default and publish screenshots, videos, logs, and traces as CI artifacts.
- Split tests by files or runner workers only after confirming that each test owns its context and test data.
- Retry only transient infrastructure failures. A retry should not hide a deterministic assertion failure.
For Chromium-only, headless CI, the browser guide documents installing the smaller shell build with --only-shell. Reinstall browsers when the Playwright dependency changes.
Common errors and fixes
| Error or symptom | Likely cause | Fix |
|---|---|---|
| Executable doesn’t exist | Browser binaries were not installed or do not match the dependency. | Run the Java CLI install command again after resolving the current Playwright version. |
| Missing shared library on Linux | OS browser dependencies are absent. | Use install --with-deps in a suitable build image or install the documented packages. |
| Timeout while clicking | The locator is wrong, the element is covered, disabled, or never rendered. | Use a role/label/test-id locator, inspect the page, wait for the real state, and fix the application or selector instead of adding a long sleep. |
| Assertion is flaky | The assertion checks an intermediate state or shares browser state. | Assert a user-visible final state, use retrying Playwright assertions, and create a fresh context per test. |
| Tests pass alone but fail together | Shared context, cookies, local storage, ports, or test data. | Isolate contexts and data; make parallel tests independent. |
| Headed mode cannot start in CI | No display server is available. | Run headless or configure the CI display environment explicitly. |
| Branded Chrome or Edge behaves differently | A global branded installation differs from the Playwright-managed browser. | Pin and document the browser choice; remember branded installs can use a global OS location and override an existing installation. |
| Popup or download event is missed | The event wait was registered after the click. | Wrap the trigger in the corresponding wait so the listener exists before the action. |
Performance, reliability, and cost notes
- Startup: Reuse a Playwright instance and browser where safe, then create contexts per test. Launching a new browser for every test is usually unnecessary overhead.
- Parallelism: Increase workers only when the CI machine, application environment, and test data can support it. Context isolation does not isolate external databases or shared accounts.
- Waiting: Prefer locator auto-waiting and retrying assertions. Fixed delays make suites slower and still fail when environments vary.
- Browser coverage: Run a focused fast suite on one browser for every change, then schedule Chromium, Firefox, and WebKit coverage according to your compatibility needs.
- Cost: Playwright itself is an open-source automation library; your practical costs are CI minutes, browser storage, test environments, and any external services the tests exercise.
- Reliability: Pin dependency versions, reinstall matching browsers after upgrades, isolate contexts, keep selectors stable, and preserve failure artifacts.
Or skip the browser setup
If your goal is a clean screenshot rather than an interactive end-to-end test, ScreenshotNeo provides a single API request. Cookie and consent banners are accepted and removed before capture, along with more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.
See the ScreenshotNeo API documentation for all options.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also supports full-page capture with lazy images loaded, CSS element capture, dark mode, device presets, custom viewport and retina scale, PDF output, custom CSS and JavaScript, clicks, selector waits, delays, network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, caching with a chosen TTL, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and an OpenAPI specification. An MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account.
FAQ
Does Playwright Java require Selenium?
No. Playwright Java is its own browser automation library and manages its supported browser engines through its Java API and browser installation CLI.
Should every test launch a new browser?
No. Reuse the browser process when practical, but create a new BrowserContext for each test to isolate state.
Can I run Firefox and WebKit tests?
Yes. Playwright Java supports Chromium, Firefox, and WebKit. Install the corresponding browser binaries for the Playwright release in your project.
Are generated Codegen tests production-ready?
They are a useful starting point. Review locators, remove exploratory actions, and add assertions that represent the behavior your team intends to protect.
When should I use ScreenshotNeo instead of Playwright?
Use Playwright when you need an interactive, stateful end-to-end test. Use ScreenshotNeo when you need a screenshot or PDF from a URL without maintaining browser setup, especially when consent UI and failed-page billing behavior matter.


