E2E and the Django test client
The Django test Client calls the view directly: there is no browser, no JavaScript and no real HTTP (Django testing basics, pytest-django setup). That is fast and enough for most checks. An E2E test uses a real browser, so it also proves that the route, template, CSRF token, session, static files and JavaScript work together.
| Test client | E2E with Playwright | |
|---|---|---|
| Runs | the view in-process | Chromium + Django over HTTP |
| JavaScript, HTMX | no | yes |
| Speed | milliseconds | seconds |
| Use for | logic, permissions, redirects | critical paths: login, order, payment |
💡 One E2E test per critical scenario; leave the details to the test client. Mark browser tests with
@pytest.mark.e2eso the daily run can skip them.
Setup
pytest-django provides the live_server fixture and pytest-playwright provides page, context and browser:
pip install pytest-django pytest-playwright
playwright install chromium
# pyproject.toml
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "config.settings.test"
markers = ["e2e: slow browser tests"]
# run only the browser tests, with a visible browser
pytest -m e2e --headedplaywright install chromium downloads the browser once. The default mode is headless; --headed shows the window, which helps when you write a test for the first time.
live_server and the first test
live_server starts Django in a separate thread on a free port and gives you live_server.url. The test thread drives Playwright, Playwright talks to Django over HTTP, and both use the same test database.
import re
import pytest
from playwright.sync_api import Page, expect
pytestmark = pytest.mark.e2e
def test_login_shows_dashboard(live_server, page: Page, user):
page.goto(f"{live_server.url}/accounts/login/")
page.get_by_label("Username").fill("ann")
page.get_by_label("Password").fill("secret123")
page.get_by_role("button", name="Sign in").click()
expect(page).to_have_url(re.compile(r"/dashboard/$"))
expect(page.get_by_role("heading", name="Dashboard")).to_be_visible()Three habits make the test stable: