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.