Skip to content

Java E2E tests with Playwright

An E2E test uses a browser against an actual HTTP server. @ShadowPg starts the application with a pgmem fork and restores its prepared data before each browser test. The HTTP server and its connection pool stay alive throughout the class.

Playwright Java with Spring Boot or Micronaut

Section titled “Playwright Java with Spring Boot or Micronaut”

Both examples use Playwright’s JUnit integration. @ShadowPg redirects the application’s normal database configuration to pgmem and restores data before each test.

OrdersE2ETest.java
@UsePlaywright
@ShadowPg(schema = "db/schema.sql")
class OrdersE2ETest {
@LocalServerPort int port;
@Test void createsAnOrder(Page page) {
page.navigate("http://127.0.0.1:" + port + "/orders");
page.getByText("New order").click();
assertThat(page.getByText("Order created")).isVisible();
}
@Test void startsFromPreparedDataAgain(Page page) {
page.navigate("http://127.0.0.1:" + port + "/orders");
assertThat(page.getByText("No orders")).isVisible();
}
}

Add pgmem-spring for Spring Boot or pgmem-micronaut for Micronaut as a test dependency. Put db/schema.sql in src/test/resources. If the app runs its own startup migration, omit schema; the post-startup state becomes the reset baseline. Use seed = "db/seed.sql" for additional initial data. Each test class owns its server and fork, and both close after the class.

Add the PostgreSQL JDBC driver and platform-specific pgmem-native dependency from Java basics too.

JUnit methods in one class should run sequentially because they share the same app and fork. Parallel classes each own an application context, pgmem fork, HTTP port, and browser fixtures. If the app runs in a separate JVM, pass the fork’s JDBC URL in its startup configuration before it builds a pool; use the same reset() lifecycle.

For HTTP-only checks without a browser, use API tests.

Run E2E methods that share one @ShadowPg HTTP app sequentially. Parallelize independent classes with a separate pgmem fork, application context, random HTTP port, and browser context for each class. A JUnit Jupiter configuration for that shape is:

junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default = same_thread
junit.jupiter.execution.parallel.mode.classes.default = concurrent

Wait for browser-triggered requests and background database work to finish before the next method resets the fork. Separate browser contexts do not protect a shared database from an overlapping reset. Bound the number of parallel classes to available memory and browser capacity.