---
title: "Test email verification with pytest and Playwright · Proxy Mail QA"
description: "A pytest test that signs up with a fresh inbox, waits for the verification code or link and finishes the flow, with a standard-library Python helper."
url: https://proxymail.qa/solve/pytest-email-tests
---

1. [Home](https://proxymail.qa/)
2. [What we solve](https://proxymail.qa/solve)

# Test sign-up emails with pytest

To test email verification in pytest, give each test a fresh address, sign up with it, then wait for the code or link and finish the flow. Proxy Mail QA's Python helper is one file using only the standard library: create\_address before the form, wait\_for\_code or wait\_for\_link after it. It works with Playwright for Python and Selenium, on the Free plan.

Updated October 17, 2026

## Set up

Copy the helper, `proxymail_qa.py`, from the [Python docs](https://proxymail.qa/docs/python) into your tests folder, and put an [API key](https://proxymail.qa/docs/api-keys) in `PROXYMAIL_QA_API_KEY`, in your shell, a `.env` file kept out of Git, or your CI’s secrets. The Free plan is enough.

## The test

With Playwright for Python:

test\_signup.py

```python
import re

from playwright.sync_api import Page, expect

from proxymail_qa import create_address, delete_address, now, wait_for_link

def test_sign_up_verifies_the_email(page: Page):
    account = create_address()
    page.goto("/signup")
    page.get_by_label("Email").fill(account["address"])
    page.get_by_label("Password").fill(account["password"])

    submitted = now()
    page.get_by_role("button", name="Sign up").click()

    page.goto(wait_for_link(account["address"], since=submitted))
    expect(page).to_have_url(re.compile("verified"))
    delete_address(account["address"])

```

For a code, use `wait_for_code(address, since=submitted)` and type what it returns. With Selenium, the calls are the same: find the fields with `driver.find_element` and send the code or open the link. `since` makes the wait ignore anything sent before the form was submitted, so a retried test never reads an old email.

## Check the email, not just the link

test\_emails.py

```python
import time

from proxymail_qa import check_email, count_emails, now, wait_for_link

def test_sign_up_sends_one_good_verification_email(page, account):
    submitted = now()
    page.get_by_role("button", name="Sign up").click()
    wait_for_link(account["address"], since=submitted)
    time.sleep(10)  # give a duplicate time to arrive
    assert count_emails(account["address"], since=submitted, type="link")["count"] == 1
    assert check_email(account["address"], since=submitted, expect_host="staging.example.com")["failed"] == []

```

`check_email` returns verdicts on SPF, DKIM and DMARC, links to localhost or plain http, where the link goes and more, never the email itself. `expect_no_email` checks that an action sends nothing, such as a second reset request that should be refused.

## Run it in CI, with a report on the pull request

Import the helper’s pytest hooks in `conftest.py`. In GitHub Actions, add the [Proxy Mail QA action](https://proxymail.qa/docs/ci#a-report-on-the-pull-request) before the tests: the report on the pull request then says which test each email came from, and lists the failed tests with what their emails did.

conftest.py

```python
from proxymail_qa import pytest_runtest_setup, pytest_runtest_teardown, pytest_runtest_logreport, pytest_sessionfinish

```

.github/workflows/e2e.yml

```yaml
      - uses: aaquib90/proxymail-qa-action@v1
        with:
          api-key: ${{ secrets.PROXYMAIL_QA_API_KEY }}
      - run: pytest
        env:
          PROXYMAIL_QA_API_KEY: ${{ secrets.PROXYMAIL_QA_API_KEY }}

```

The hooks do nothing outside a run, and never fail your tests themselves. More in the [Python docs](https://proxymail.qa/docs/python) and [Running in CI](https://proxymail.qa/docs/ci).

## Try it on your next sign-up test

Free plan, no credit card. Create an address and watch the code or link arrive.

[Create a free account](https://app.proxymail.qa/?mode=signup)

## Questions

### Is there a pip package?

Not yet. The helper is a single file using only Python's standard library: copy it from the Python docs into your test folder, and there's nothing to install or keep updated.

### Does it work with Selenium?

Yes. The helper doesn't touch the browser: it creates the address and returns the code or link, and your Selenium or Playwright code types or opens it.

### What if the email never arrives?

The wait raises ProxyMailQAError saying why as far as Proxy Mail QA can tell: most often the app's email provider is in sandbox or test mode and only sends to verified addresses. Fix the sending side rather than waiting longer.

## Related guides

- [Test sign-up and email verificationA fresh, private address for every sign-up test, with the verification link or code ready the moment it lands.](https://proxymail.qa/solve/email-verification-testing)
- [Test OTP and one-time login codesCodes pulled out of the email with a Copy button and a countdown when the email says how long they last.](https://proxymail.qa/solve/otp-code-testing)
- [Test magic links and passwordless sign-inSingle-use links that are never opened or prefetched on our side, so they still work when you click them.](https://proxymail.qa/solve/magic-link-testing)
- [Test password reset emailsRequest, receive and use reset links, and keep the new password saved next to the test account.](https://proxymail.qa/solve/password-reset-testing)
- [Stop sharing one Gmail inbox for QAPrivate, disposable addresses for every tester instead of one shared mailbox and a pile of plus-addresses.](https://proxymail.qa/solve/shared-test-inbox)
- [Take QA notes by voice with Wispr FlowDictate what each test account is for, its state and what you found, straight into its notes, in seconds.](https://proxymail.qa/solve/voice-notes-wispr-flow)
- [Verification link points to localhost:3000Why emails from staging link to localhost, how to fix it in Supabase, Firebase and other stacks, and how we flag it.](https://proxymail.qa/solve/localhost-links)
- [Test sign-up emails in PlaywrightA complete Playwright test for sign-up codes and links, from a fresh inbox to the report on the pull request.](https://proxymail.qa/solve/playwright-email-tests)
- [Test sign-up emails in CypressA complete Cypress test for sign-up links and codes, with the email waits running as Cypress tasks.](https://proxymail.qa/solve/cypress-email-tests)

[Built your app with an AI tool? Pick yours](https://proxymail.qa/built-with)
