pytestintermediate

pytest Transactional Email Test

Test transactional emails in Python with pytest

pytest Transactional Email Test (python)python
# tests/conftest.py
import pytest
import requests
import os
import time

@pytest.fixture
def plop():
    """Fixture providing plop.email API client."""
    class PlopClient:
        def __init__(self):
            self.base_url = "https://api.plop.email/v1"
            self.api_key = os.environ["PLOP_API_KEY"]

        def get_latest(self, to: str, retries: int = 5, delay: float = 1.0):
            """Fetch latest email with retry logic."""
            for attempt in range(retries):
                response = requests.get(
                    f"{self.base_url}/messages/latest",
                    params={"to": to},
                    headers={"Authorization": f"Bearer {self.api_key}"},
                )
                data = response.json()
                if response.status_code == 200 and "error" not in data:
                    return data
                time.sleep(delay)
            raise TimeoutError(f"Email not received after {retries} attempts")

        def get_all(self, mailbox: str):
            """Fetch all emails for a mailbox."""
            response = requests.get(
                f"{self.base_url}/messages",
                params={"mailbox": mailbox},
                headers={"Authorization": f"Bearer {self.api_key}"},
            )
            return response.json()

    return PlopClient()


@pytest.fixture
def test_email():
    """Generate unique test email address."""
    def _generate(prefix: str = "test"):
        timestamp = int(time.time() * 1000)
        return f"{prefix}+{timestamp}@in.plop.email"
    return _generate


# tests/test_emails.py
import pytest
from myapp.email import send_order_confirmation, send_shipping_notification


class TestOrderEmails:
    def test_order_confirmation_includes_items(self, plop, test_email):
        """Order confirmation should list all items with prices."""
        email = test_email("order")
        order = {
            "id": "ORD-123",
            "items": [
                {"name": "Widget", "price": 29.99, "qty": 2},
                {"name": "Gadget", "price": 49.99, "qty": 1},
            ],
            "total": 109.97,
        }

        send_order_confirmation(to=email, order=order)

        received = plop.get_latest(email)

        assert "Order Confirmation" in received["subject"]
        assert "ORD-123" in received["htmlContent"]
        assert "Widget" in received["htmlContent"]
        assert "$29.99" in received["htmlContent"]
        assert "$109.97" in received["htmlContent"]

    def test_shipping_notification_has_tracking(self, plop, test_email):
        """Shipping notification should include tracking link."""
        email = test_email("shipping")

        send_shipping_notification(
            to=email,
            order_id="ORD-123",
            tracking_number="1Z999AA10123456784",
            carrier="UPS",
        )

        received = plop.get_latest(email)

        assert "shipped" in received["subject"].lower()
        assert "1Z999AA10123456784" in received["htmlContent"]
        assert "track" in received["htmlContent"].lower()

    @pytest.mark.parametrize("locale,expected", [
        ("en", "Your order has shipped"),
        ("es", "Tu pedido ha sido enviado"),
        ("fr", "Votre commande a été expédiée"),
    ])
    def test_shipping_localization(self, plop, test_email, locale, expected):
        """Shipping emails should be localized."""
        email = test_email(f"locale-{locale}")

        send_shipping_notification(
            to=email,
            order_id="ORD-123",
            tracking_number="TRACK123",
            carrier="UPS",
            locale=locale,
        )

        received = plop.get_latest(email)
        assert expected in received["htmlContent"]

How It Works

1

pytest Fixtures

We create reusable fixtures for the plop client and test email generation. Fixtures keep tests DRY and readable.

2

Retry Logic

The get_latest method includes retry logic to handle email delivery delays gracefully.

3

Parametrized Tests

The localization test uses @pytest.mark.parametrize to test multiple languages with the same test logic.

4

Real Data Testing

We test with realistic order data to verify the email template handles real-world content correctly.

Try This Example

Get a free API key and test this example in minutes.