DEV Community

Silviu Technology
Silviu Technology

Posted on

How to Stop Playwright Email Tests From Flaking Across Parallel Workers

Parallel Playwright runs are great until email assertions join the party. A suite passes on one worker, fails on four workers, then passes again on retry. In most teams I have helped debug, the real problem is not Playwright itself. The problem is a shared inbox, weak message filtering, or a retry that reuses data from the first attempt.

When I review flaky end-to-end suites, email verification is one of the first places I look. The fix is usually boring in a good way: one inbox per test worker or per test run, explicit message matching, and short-lived cleanup. If you need a quick non-production inbox source, a tempmailso workflow can support that pattern, but the reliability gain comes from isolation and traceability, not from any single tool.

Why parallel workers make email tests flaky

Playwright runs tests in isolated workers and can retry failures in a fresh worker process, which is exactly what you want for stable automation: https://playwright.dev/docs/test-parallel and https://playwright.dev/docs/test-retries

The catch is that your app under test does not know or care about Playwright worker boundaries. If worker 1 and worker 3 both write signup emails into the same mailbox, your assertion layer can easily grab the wrong message. That creates three common failure modes:

  • a test reads an older email that still matches the subject
  • a retry passes because the first failed attempt already triggered the message
  • two workers race on the same account and nobody can prove which link belonged to which run

This is why a burner email generator only helps when you connect it to worker identity. If the generated inbox is still shared by several tests, the flake just moves around. I have seen teams keep a fallback note like "check tamp mail com if the first inbox looks empty," and that is usually the moment the investigation starts getting muddy.

Related patterns show up in oauth recovery inbox isolation and preview invite email checks. Different flows, same root cause: inbox collisions hide whether the product or the test is broken.

The inbox pattern that keeps retries honest

The most reliable setup is simple:

  1. Generate a unique application user for the test.
  2. Generate a unique inbox alias for that exact test or worker.
  3. Trigger one product action that should send one email.
  4. Poll only that inbox for a narrow time window.
  5. Assert on recipient, subject, and message body before you click anything.
  6. Delete or expire the inbox after the check ends.

I prefer making the alias include something like the worker index, test id, and timestamp. That way, if a failure happens, you can map the email back to the exact run without guesswork. It sounds tiny, but it saves a lot of QA time later.

Another important detail is retry behavior. If a test fails after triggering the email, the retry should create a new identity and a new inbox instead of reusing the first one. Reusing the same address makes the second attempt hard to reason about, because now the mailbox contains mixed evidence. Teams sometmes blame the enviroment for this, but the test data design is the real issue.

You should also store the inbox id in your test logs, not the full confirmation link. Logging the link can leak secrets into CI output. Logging the inbox id keeps the run auditable while still being safer.

What to assert in Playwright before you trust the email

Once the right message is isolated, I like checking more than "email received":

  • the recipient matches the test identity exactly
  • the message was created after the test started
  • the subject line reflects the triggered action
  • the confirmation URL points at the expected domain and env
  • the token or code works once and fails after reuse
  • no second unexpected email appears for the same action

This matters for signup verification, passwordless login, invite acceptance, and tem email validation flows alike. A passing inbox check is not enough if the link is stale, duplicated, or pointing to the wrong place.

For Playwright, keep the email polling helper separate from UI steps. I want the UI action to trigger the send, then a helper to fetch and parse the message, then a separate assertion block for the link or code. That separation makes failures easier to classify. Was the issue send timing, mailbox lookup, or product behavior after click? If all three are jammed into one helper, the stack trace gets noisy real fast.

A repeatable debugging checklist for flaky email runs

When a suite flakes, I go through this checklist before changing timeouts:

  • confirm every parallel test gets a seprate inbox
  • confirm retries generate new inboxes instead of reusing old ones
  • check subject filters are specific enough for the feature under test
  • verify old messages are purged between runs
  • record send timestamp, inbox id, and worker id in logs
  • validate that cleanup happens even after assertion failures

If those six items are solid, most email flakes become easier to reproduce or disappear entirely. If they are not solid, adding sleeps usually just hides the bug for a week.

One more caution: avoid creating a "shared diagnostic inbox" that engineers manually inspect after failures. It feels practical, but it quietly reintroduces the same ambiguity you were trying to remove. Thats why I prefer automated capture plus short retention over manual mailbox archaeology.

Q&A

Should I use one inbox per worker or one inbox per test?

One inbox per test is safest. One inbox per worker can still work when each worker operates on fully unique data and only one email-producing action happens at a time, but per-test isolation is easier to trust.

Do I need a different inbox on every retry?

Yes. Otherwise the retry can pass against leftovers from the first attempt, which makes the result less believable.

What is the first signal that a flaky email test has a data-isolation problem?

The biggest clue is a test that fails only in parallel or only on retry. When that happens, inspect inbox ownership before you touch timeout values, because the timing problem is often only a sympton.

Top comments (0)