Testing a sequence of emails
Some flows send more than one message: sign up, then welcome, then a receipt. Read the whole thread and assert on the order.
How it works
messages returns every message in the inbox, newest first. That ordering is the thing to be careful about: the mail your flow sent last is at index zero, and the one that started it is at the end.
Read them all
Newest first — reverse when you want to reason in the order the user experienced them.
all_msgs = inbox.messages() # newest first
in_order = list(reversed(all_msgs)) # as the user saw them
assert [m.subject for m in in_order] == ["Verify your email", "Welcome", "Your receipt"]
Wait for the one you want
When several messages are in flight, poll until the one you care about shows up rather than grabbing the latest.
import time
def wait_for_subject(inbox, needle, timeout=60):
deadline = time.time() + timeout
while time.time() < deadline:
for m in inbox.messages():
if needle in (m.subject or ""):
return m
time.sleep(2)
raise AssertionError(f"no message titled {needle!r} within {timeout}s")
See also
Waiting and timeouts
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.
Message propertiesThe envelope around the body: who sent it, exactly which address it reached, what the subject line said and when it landed.
Tags and plus-addressingOne inbox, many senders: add a plus tag to the address and MailFlat records it as a field you can filter on.