Fixtures
Table of contents
Introduction
The framework’s fixtures build the app and reset the database. Your feature adds its own on top, for its test data. Import the framework’s in your conftest.py.
# splent_feature_notes/tests/conftest.py
from splent_framework.fixtures.fixtures import * # noqa: F401,F403
Using the framework’s fixtures
test_client (function scope)
A Flask test client with a clean database for every test. Use it for most tests.
def test_the_index_loads(test_client):
assert test_client.get("/notes/").status_code == 200
test_app (session scope)
The product’s app, built once with its testing configuration. Use it when a data fixture needs the app, or a test needs its config.
The test app keeps its files in a temporary store, removed when the test process ends, never where the product keeps them. PROTECTED_UPLOADS_DIR, UPLOADS_DIR, the instance folder and the static uploads folder all point there. To give a test a folder of its own, set the variable.
def test_a_private_file(test_client, monkeypatch, tmp_path):
monkeypatch.setenv("PROTECTED_UPLOADS_DIR", str(tmp_path))
...
The other fixtures
test_client_moduleshares one database across a file, for tests that form a sequence. They depend on their order, so default to function scope.clean_databaseresets the database inside a test, where you choose.no_access_policiesruns a test with no access policies registered, to test your feature’s own role gate.
Every reset drops every table of the test database and creates the app’s, so no table keeps rows from another product’s tests. splent_migrations stays.
Writing your own fixtures
# splent_feature_notes/tests/conftest.py
from splent_framework.fixtures.fixtures import * # noqa: F401,F403
import pytest
from splent_io.splent_feature_notes.models import Notes
from splent_framework.db import db
@pytest.fixture(scope="function")
def sample_note(test_app):
with test_app.app_context():
note = Notes(title="First note", content="Hello", published=True)
db.session.add(note)
db.session.commit()
db.session.expunge(note)
return note
@pytest.fixture(scope="function")
def note_page(test_client, sample_note):
return test_client.get(f"/notes/{sample_note.id}", follow_redirects=True)
db.session.expunge() detaches the object, so a test can use it without DetachedInstanceError. Expunge every object a fixture returns. pytest resolves the chain, from test_app through test_client and sample_note to note_page.
Using another feature’s models
from splent_io.splent_feature_notes.models import Notes
The tests of reports, which requires notes, import its models. feature:test puts every feature your product declares on PYTHONPATH, at the version it pins. See Which copy is tested.
Choosing a scope
| Scope | Resets the database | Use for |
|---|---|---|
function |
Per test | Independent tests, the default |
module |
Per file | Sequential flows |
session |
Once | Building the app only |
Never create data at session scope, and match a fixture’s scope to its client’s.
See also
- Test levels, where to use each fixture
- Test environment, how the test database is set up