blog
Spring Boot + Playwright testing for Java and Kotlin teams
Build reliable browser tests for Spring Boot apps with Playwright, then verify email outcomes with isolated inbox APIs.
Spring Boot teams often have stable controller tests and flaky end-to-end tests. Playwright can improve browser reliability, but the biggest production failures usually happen in the email step after form submission.
This guide shows a practical setup for Java/Kotlin teams that need both browser confidence and message verification.
Architecture at a glance
| Layer | Tooling | Purpose |
|---|---|---|
| App runtime | Spring Boot test context | Start real app flows on a random local port |
| Browser automation | Playwright | Drive forms, navigation, and UI assertions |
| Message verification | MailSlurp inbox API | Wait for real email and assert token/link behavior |
| Release signal | CI pipeline | Fail builds when user-critical messaging paths break |
1) Start Spring Boot on a random test port
Use RANDOM_PORT so each test process can run in parallel without fixed-port collisions.
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SignupFlowTest {
@LocalServerPort
var port: Int = 0
}
This gives you a deterministic target URL per run: http://localhost:$port.
2) Add Playwright to your test dependencies
Gradle
dependencies {
testImplementation("com.microsoft.playwright:playwright:1.52.0")
}
Maven
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.52.0</version>
<scope>test</scope>
</dependency>
3) Build a minimal reusable Playwright harness
companion object {
private lateinit var playwright: Playwright
private lateinit var browser: Browser
@JvmStatic
@BeforeAll
fun setupClass() {
playwright = Playwright.create()
browser = playwright.chromium().launch()
}
@JvmStatic
@AfterAll
fun teardownClass() {
browser.close()
playwright.close()
}
}
private lateinit var context: BrowserContext
private lateinit var page: Page
@BeforeEach
fun setupTest() {
context = browser.newContext()
page = context.newPage()
}
@AfterEach
fun teardownTest() {
context.close()
}
4) Add email verification to the browser journey
Browser-only assertions miss common production failures:
- email never arrives,
- verification link has wrong tenant/environment host,
- token is malformed or expired.
Pattern:
- Create a fresh inbox for this test.
- Submit signup form in Playwright with that address.
- Wait for verification email via API.
- Extract link or code.
- Continue browser flow and assert activated state.
val inbox = mailslurp.inboxController.createInbox()
page.navigate("http://localhost:$port/signup")
page.fill("#email", inbox.emailAddress)
page.click("button[type='submit']")
val email = mailslurp.waitController.waitForLatestEmail(
WaitForLatestEmailOptions().inboxId(inbox.id).timeout(60_000L)
)
val links = mailslurp.emailController.getEmailLinks(email.id)
val verifyLink = links.links.first()
page.navigate(verifyLink)
assertThat(page.textContent("h1")).contains("Account verified")
5) Stabilize CI before scaling suite count
Use these controls before adding more scenarios:
- one inbox per test (or per worker),
- explicit API wait timeouts by flow type,
- trace/screenshot on failure,
- artifact capture for inbox ID + message ID + extracted token/link.
This keeps incident triage fast when failures happen.
Playwright vs MockMvc vs contract tests
MockMvc: fastest for server-side behavior and validation logic.Playwright: best for user-journey/browser-state confidence.- MailSlurp API assertions: best for message-delivery correctness and verification tokens.
Use all three where risk is highest.
Related routes
- Playwright browser automation guide
- Email integration testing
- How to test emails in development
- Email for testing operating model
- Email Sandbox
Final take
For Spring Boot teams, Playwright is strongest when combined with real message verification. Treat email as part of the transaction boundary, not a side effect, and your release confidence improves materially.