Selenium email testing
Selenium suites usually stop at did the app say “check your inbox”. With a dozen lines of Python they can assert on the actual email: right sender, right subject, right code. The inbox side is a free REST API — no SMTP server, no Gmail app password.
The inbox helper
# tempx_helper.py
import re, time, requests
TEMPX = "https://tempx.uk/api/inbox"
def wait_for_message(address, subject_re=None, timeout=45, interval=2):
"""Poll the Tempx inbox API until a matching message arrives."""
deadline = time.time() + timeout
pattern = re.compile(subject_re, re.I) if subject_re else None
while time.time() < deadline:
resp = requests.get(TEMPX, params={"address": address}, timeout=10)
resp.raise_for_status()
for msg in resp.json()["messages"]:
if pattern is None or pattern.search(msg["subject"] or ""):
return msg
time.sleep(interval)
raise TimeoutError("no matching email for " + address + " within "
+ str(timeout) + "s")
A complete test
# test_signup.py
import re, time
from selenium import webdriver
from selenium.webdriver.common.by import By
from tempx_helper import wait_for_message
def test_signup_sends_verification_code(driver):
addr = "sel-" + str(int(time.time())) + "@tempx.uk"
driver.get("https://staging.example.com/signup")
driver.find_element(By.ID, "email").send_keys(addr)
driver.find_element(By.ID, "password").send_keys("correct-horse-battery")
driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
msg = wait_for_message(addr, subject_re=r"verify")
assert "noreply@example.com" in msg["from"]
code = re.search(r"\b(\d{6})\b", msg["preview"]).group(1)
driver.find_element(By.ID, "code").send_keys(code)
driver.find_element(By.XPATH, "//button[.='Verify']").click()
assert "Welcome" in driver.page_source
Notes for real suites
- Explicit waits still apply. After submitting the code, wait for the success element rather than sleeping — the email part is now deterministic, keep the UI part that way too.
- xdist-friendly addressing. With pytest-xdist, include the worker id in the address (
sel-gw0-…) so parallel workers are hermetic by construction. - Reads are unauthenticated. Named inboxes need no secrets in CI; set
requeststimeouts as shown and back off on 429s (240 reads/minute cap). - Same inbox, other eyes. The captured mail can also be inspected in the web app, pushed to Telegram, or read by an agent over MCP while the suite runs.
Give the suite a mailbox
Drop the helper in, point it at your staging signup, and delete the last time.sleep(20).