Waiting for email without flaky tests
Mail is asynchronous, so every email test waits for something. The difference between a solid suite and a flaky one is how that wait is written.
How it works
Three rules. Always set a deadline. Fail loudly when the deadline passes, naming the address you were waiting on. Never sleep a fixed number of seconds and hope — that is either slower than it needs to be or shorter than it needs to be, and usually both on different days.
Wait with a deadline
The official clients poll for you and raise when the deadline passes.
otp = inbox.wait_for_otp(timeout=60, poll_interval=2) # seconds
msg = inbox.wait_for_message(timeout=60)
Fail loudly, not silently
A helper that returns nothing on timeout turns a missing email into a confusing error three lines later.
from mailflat import OTPTimeoutError
try:
otp = inbox.wait_for_otp(timeout=60)
except OTPTimeoutError:
pytest.fail(f"no code reached {inbox.address} in 60s")
Or stop polling entirely
If your runner can accept an inbound request, let the message come to you instead.
# register once, then react instantly instead of polling
curl -s -X POST https://mailflat.net/api/webhooks \
-H "Authorization: Bearer $MAILFLAT_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url":"https://ci.example.com/hooks/mailflat"}'
Webhooks are registered with a session token, not an account key. Full setup, payload shape and signature verification: see the webhooks guide.
Worth knowing
Polling budget is rarely the problem
The limit that matters is 500 requests per hour per IP. At a two to three second interval with a real deadline, a suite of hundreds of tests stays comfortably inside it.
See also
One-time codes
MailFlat pulls the verification code out of the message for you and hands it over as a field, so your test never runs a regex against an email body.
WebhooksPush incoming mail to your own endpoint instead of polling: registration, payload shape, signature verification and retry behaviour.
Testing & CIGive every CI run its own real inbox: the four API calls, parallel shards, teardown, and how to keep an end-to-end suite fast and honest.