MailFlatDocs
Documentation/Test frameworks & CI/pytest

Email testing with pytest

A conftest fixture that gives every test its own inbox and cleans up afterwards, whether the test passed or not.

How it fits together

pytest fixtures are the natural home for this: one inbox per test, created before and deleted after, with the address handed straight to the test function.

Skip the setup

The steps below are what a working project looks like. If you want that project rather than the explanation, generate it — one runnable smoke test, one signup test to edit, and a gitignored .env for your key.
Shell
npm create mailflat@latest -- --template pytest
Zero dependencies of its own; the generated project pulls the SDK.

The fixture

Everything after the yield is teardown, so a failing test still cleans up.
Python
# conftest.py
import os, uuid, pytest
from mailflat import MailFlat
 
@pytest.fixture(scope="session")
def mf():
return MailFlat(api_key=os.environ["MAILFLAT_API_KEY"])
 
@pytest.fixture
def inbox(mf):
box = mf.create(prefix=f"pytest-{uuid.uuid4().hex[:8]}", retention_hours=2)
yield box
box.delete()

The test

Python
# test_signup.py
def test_signup_sends_a_verification_code(inbox, client):
client.post("/signup", json={"email": inbox.address})
 
otp = inbox.wait_for_otp(timeout=60)
assert client.post("/verify", json={"code": otp}).status_code == 200

Run it in parallel

pytest-xdist splits tests across processes. Because each test owns its inbox, nothing else has to change.
Shell
pytest -n 4

Worth knowing

Keep the client session-scoped and the inbox function-scoped: one HTTP client, many inboxes.
wait_for_otp raises OTPTimeoutError, which pytest reports as a failure with the address in the message.