plain.pytest
Run tests with pytest and useful fixtures for Plain applications.
Overview
You can run tests using the plain test command, which wraps pytest and automatically loads environment variables from .env.test if it exists.
plain test
Any additional arguments are passed directly to pytest.
plain test -v --tb=short
plain test tests/test_views.py
A basic test looks like this:
from plain.test import Client
def test_homepage():
client = Client()
response = client.get("/")
assert response.status_code == 200
The Client class comes from plain.test and lets you make requests to your app without starting a server.
Fixtures
settings
The settings fixture provides access to your Plain settings during tests. Any modifications you make are automatically restored when the test completes.
def test_debug_mode(settings):
settings.DEBUG = True
assert settings.DEBUG is True
# After this test, DEBUG is restored to its original value
testbrowser
The testbrowser fixture gives you a TestBrowser instance that wraps Playwright and runs a real Plain server in the background. This is useful for end-to-end browser testing.
def test_login_page(testbrowser):
page = testbrowser.new_page()
page.goto("/login/")
assert page.title() == "Login"
The browser connects to a test server running over HTTPS on a random available port. Self-signed certificates are generated automatically.
Authentication helpers
You can log in a user without going through the login form using force_login.
def test_dashboard(testbrowser, user):
testbrowser.force_login(user)
page = testbrowser.new_page()
page.goto("/dashboard/")
assert "Welcome" in page.content()
To log out, use logout, which clears all cookies.
def test_logout_clears_session(testbrowser, user):
testbrowser.force_login(user)
testbrowser.logout()
page = testbrowser.new_page()
page.goto("/dashboard/")
assert "Login" in page.content()
URL discovery
The discover_urls method crawls your site starting from given URLs and returns all discovered internal links. This is useful for smoke testing.
def test_no_broken_links(testbrowser, user):
testbrowser.force_login(user)
urls = testbrowser.discover_urls(["/"])
assert len(urls) > 0
Database isolation
If plain.models is installed, the testbrowser fixture automatically uses the isolated_db fixture and passes the database connection to the test server. This means your browser tests and your test code share the same database state.
FAQs
Do I need Playwright installed?
The testbrowser fixture requires playwright and pytest-playwright to be installed, but they are not dependencies of this package. If you only use the settings fixture, you don't need Playwright.
uv add playwright pytest-playwright --dev
playwright install
How do I use a .env.test file?
Create a .env.test file in your project root with test-specific environment variables. The plain test command automatically loads it before running pytest.
# .env.test
DATABASE_URL=postgres://localhost/myapp_test
SECRET_KEY=test-secret-key
How do I run a specific test?
Pass the test path and any pytest options after plain test.
plain test tests/test_views.py::test_homepage -v
Installation
Install the plain.pytest package from PyPI:
uv add plain.pytest --dev
Create a conftest.py in your tests directory to make the fixtures available:
# tests/conftest.py
from plain.pytest.plugin import settings, testbrowser # noqa: F401
If you're using the testbrowser fixture, also install Playwright:
uv add playwright pytest-playwright --dev
playwright install chromium