Fixtures are composable, scope-aware dependency injection instead of fixed per-class hooks
unittest setUp and tearDown are fixed hooks tied to a TestCase class. They run for every test method, they cannot be parameterized per test, and sharing them across classes requires inheritance. pytest fixtures are functions that a test requests by name, so setup is composed by dependency injection rather than by inheritance. A fixture can be function, class, module, package, or session scoped, which means an expensive resource like a database container can be created once per session and injected everywhere. Fixtures can depend on other fixtures, so a db_session can depend on a connection which depends on a container. Teardown is expressed as code after the yield inside the fixture, and pytest guarantees it runs even if the test fails.
Request fixtures by naming them in the test signature; pytest resolves the dependency graph.
Scope controls lifetime: function (default), class, module, package, or session.
Fixtures can be parameterized, which generates one test per parameter set without duplicating code.
conftest.py exposes fixtures to an entire directory tree without imports.
autouse fixtures apply implicitly when you need global setup such as environment variables.
Trade-off: session-scoped fixtures are fast but shared state can leak between tests. Function-scoped fixtures are safer but slower.
Common mistake: putting mutable state in a session fixture and having one test corrupt another.
Common mistake: calling teardown code after yield that can itself raise, masking the original test failure.
Version note: fixtures have been core to pytest since early versions. pytest 7+ and 8+ refined scoping and warning behavior but the API is stable.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience