7. Testing¶
rational.testing drives your pages through the real HTTP endpoints (sessions, CSRF,
validation, patches) without a browser. Install the testing extra and pytest.
tests/conftest.py¶
"""Test setup: import the project like ``python app.py`` does, with a throwaway database."""
import os
import sys
import tempfile
from pathlib import Path
PROJECT_DIR = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT_DIR))
# Settings are read when the app is imported, so point them at a temporary database first.
os.environ["DATABASE_URL"] = f"sqlite+aiosqlite:///{(Path(tempfile.mkdtemp()) / 'test.db').as_posix()}"
tests/test_guestbook.py¶
from app import app
from rational.testing import TestClient
def test_sign_the_guestbook():
with TestClient(app) as client: # "with" runs the startup hooks: the tables get created
page = client.open("/")
page.submit({"name": "Ada", "message": "Hello from the tests!"})
assert "Hello from the tests!" in page.text
def test_validation_messages():
with TestClient(app) as client:
page = client.open("/")
data = page.submit({"name": "", "message": "Visit https://spam.example"})
fields = {error["field"] for error in data["error"]["details"]}
assert fields == {"name"} # the form model rejects the empty name first
data = page.submit({"name": "Bot", "message": "Visit https://spam.example"})
assert data["error"]["details"] == [{"field": "message", "message": "Links are not allowed"}]
def test_likes_are_per_card():
with TestClient(app) as client:
page = client.open("/")
page.submit({"name": "Grace", "message": "First"})
page.submit({"name": "Alan", "message": "Second"})
page.click("♥ 0")
assert page.text.count("♥ 1") == 1 and page.text.count("♥ 0") >= 1
def test_the_json_api():
with TestClient(app) as client:
client.http.post("/api/guestbook/sign", json={"name": "API", "message": "Signed over HTTP"})
entries = client.http.get("/api/guestbook/entries").json()
assert entries[0]["message"] == "Signed over HTTP"
$ pytest
.... [100%]
What you can do with a page¶
page.click("♥ 0")Finds the element handling clicks whose accessible name (
aria-label, or its text) contains the text, and fires the event.page.input("Your name", "Ada")Types into the control labelled “Your name”.
page.submit({...})Submits a form with the given fields.
page.text/page.html/page.domThe page after every patch so far.
Each action returns the server’s JSON response, so you can also assert on validation errors
or navigation. Use with TestClient(app) when the app has startup hooks, such as creating
tables.
Next: 8. Deploying.