Fixture Fundamentals
Fixture Fundamentals
A fixture is a named setup function that tests request by parameter name:
# conftest.py — discovered automatically by pytest
import pytest
@pytest.fixture
def db(tmp_path):
conn = connect(tmp_path / "test.db")
yield conn # everything after yield is teardown
conn.close()
def test_insert(db):
db.execute("INSERT INTO t VALUES (1)")
assert db.fetch_one() == (1,)
Because db lives in conftest.py, every test in the directory tree gets it without imports. Teardown runs even when the test fails, mirroring try/finally. Fixtures compose: a fixture can request other fixtures by name, building layered setup (app → client → authed_client) without copy-paste.
Scopes and Parametrization
Scopes and Parametrization
Scopes control reuse: function (default, fresh per test), class, module, package, session (one per run).
@pytest.fixture(scope="session")
def live_api():
with TestClient(app) as c: # expensive: build once for the whole session
yield c
@pytest.mark.parametrize("n,expected", [(0, 1), (1, 1), (5, 120)])
def test_factorial(n, expected):
assert factorial(n) == expected
@pytest.fixture(params=[3338, 4450])
def port(request):
return request.param # fixture runs once per param
The scope rule: a fixture may only request fixtures of the same or broader scope. A session-scoped fixture asking for a function-scoped one errors at collection — setup order must be session → module → function, never backwards.
Pitfalls and Interview Angles
Pitfalls and Interview Angles
State leaking across tests. A session-scoped fixture that mutates shared state makes test order matter — the #1 cause of "passes alone, fails in suite." Keep shared fixtures read-mostly, or drop them to function scope.
Overrides. Redefining a fixture with the same name in a narrower conftest.py (or inside a test module) shadows the outer one — the built-in way to substitute a fake.
Visibility. pytest --setup-show prints the setup/teardown stack per test; pytest --fixtures lists what is available; -k plus --lf (last-failed) narrows a loop fast.
Interview angle: explain how pytest resolves a fixture — name lookup through conftest hierarchy, dependency graph construction, scope-ordered execution, teardown in reverse — and why autouse should stay rare: it hides setup cost in tests that never asked for it.
Quiz
1. What does yield inside a fixture provide?
2. A session-scoped fixture needs a function-scoped one. What happens?
3. How do you replace a fixture for one module only?
Flashcards
Question
yield fixture
Click to reveal answer
Answer
Provides the resource before yield; teardown code after yield always runs.
Question
Fixture scopes
Click to reveal answer
Answer
function < class < module < package < session — wider scope reuses setup more.
Question
Scope dependency rule
Click to reveal answer
Answer
A fixture may request only same-or-broader-scoped fixtures, never narrower.
Question
pytest --setup-show
Click to reveal answer
Answer
Prints fixture setup/teardown ordering per test — first tool when state seems leaked.
Revision Notes
Key Takeaways
- 1. Put reusable fixtures in conftest.py; use yield for guaranteed teardown
- 2. Scope wider only for genuinely expensive, read-only setup to avoid order-dependent tests
- 3. Parametrize combinatorics and override fixtures by redefining names in narrower scopes
Interview Tips
- • Walk fixture resolution: conftest lookup → dependency graph → scope-ordered setup → reverse teardown
- • Explain how you would debug a flaky suite: --setup-show, scope audit, isolate shared mutable state
Cheat Sheet
@pytest.fixture + yield = teardown; conftest.py shares; scopes function→session; same-or-broader requests only; @pytest.mark.parametrize; --setup-show.
Continue Learning
Before You Start
- No prerequisites listed.