If your application sends email β signup confirmations, password resets, invoices, notifications β you need automated tests that verify those emails actually arrive and contain the correct content. We have seen too many teams skip email testing because it feels complicated. It is not, and this guide will prove it.
The fundamental problem with testing email flows is that you need real, reachable inboxes that you can programmatically read. Using personal Gmail accounts in CI pipelines is fragile, insecure, and does not scale. This is exactly where disposable email APIs earn their place in a mature testing stack.
Setting Up Your Test Environment First, grab your API key from the EvilMail dashboard. Store it as an environment variable in your CI system β never commit API keys to version control. ``` export EVILMAIL_API_KEY="your_api_key_here" ```
Creating a Test Email Address Before each test run, generate a fresh disposable email: ``` curl -X POST https://evilmail.pro/api/temp-email \ -H 'Content-Type: application/json' \ -H 'X-API-Key: '"$EVILMAIL_API_KEY" \ -d '{"domain": "evilmail.pro", "ttlMinutes": 30}' ```
The response includes the email address and a session token. Use the email address to register a test account in your application.
Polling the Inbox After triggering an email-sending action in your app (registration, password reset, etc.), poll the inbox endpoint until the message arrives: ``` curl https://evilmail.pro/api/inbox \ -H 'X-API-Key: '"$EVILMAIL_API_KEY" ```
In a real test, you would wrap this in a retry loop with a timeout. Here is a practical Python example: ``` import requests import time
API_KEY = os.environ['EVILMAIL_API_KEY'] BASE = 'https://evilmail.pro'
def wait_for_email(subject_contains, timeout=60): deadline = time.time() + timeout while time.time() < deadline: r = requests.get(f'{BASE}/api/inbox', headers={'X-API-Key': API_KEY}) messages = r.json().get('data', []) for msg in messages: if subject_contains in msg['subject']: return msg time.sleep(3) raise TimeoutError('Email not received') ```
Integrating with CI/CD Pipelines This pattern works cleanly with any CI system β GitHub Actions, GitLab CI, Jenkins, CircleCI. The key principle is: each test run gets a unique disposable address, runs its assertions, and the address auto-expires. No cleanup scripts, no shared mailboxes getting cluttered, no flaky tests caused by leftover emails from previous runs.
A GitHub Actions workflow step might look like this: ``` - name: Run email integration tests env: EVILMAIL_API_KEY: ${'{ secrets.EVILMAIL_API_KEY }'} run: pytest tests/email/ -v --timeout=120 ```
What to Assert Do not just check that an email arrived. Verify the subject line, the sender address, the presence of critical links (verification URLs, reset tokens), and that those links actually resolve to valid pages. A complete email test validates the entire chain from trigger to delivery to content correctness.
Scaling to Hundreds of Tests For large test suites that need many simultaneous email addresses, use the account creation endpoint to spin up addresses under your own domain. This gives you full control over the namespace and avoids any rate limiting on the temp-email endpoint: ``` curl -X POST https://evilmail.pro/api/accounts \ -H 'X-API-Key: '"$EVILMAIL_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"email": "[email protected]", "password": "testpass"}' ```
After the test suite completes, bulk-delete the test accounts: ``` curl -X DELETE https://evilmail.pro/api/accounts \ -H 'X-API-Key: '"$EVILMAIL_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"emails": ["[email protected]"]}' ```
The Bottom Line Email testing should not be the fragile afterthought in your test suite. With a proper disposable email API, it becomes as deterministic and reliable as any other integration test. Ship with confidence that your emails actually work.
