Diagnosing Flaky Pytest Runs From Fixture Scope and Teardown Failures
Tests pass alone but fail in the full suite due to fixture scope or teardown issues. This guide walks through diagnosing shared state leaks, ordering checks, fixes, and when to raise the problem.
15 Dec 2025, 06:54 UTC

Problem: tests pass alone, fail together
The recognizable condition is that a test succeeds when run in isolation with pytest -k test_name but fails when the full suite is executed. Typical error messages include "resource already in use", stale database rows, or leftover files that persist until the pytest process is restarted. The failure usually appears after a particular test and remains until the next run.
Flakiness often stems from shared state that leaks between tests. Pytest fixtures control setup and teardown through the scope parameter and finalizer callbacks. When a fixture’s scope is too broad, when autouse=True is used inadvertently, or when a teardown raises an exception, the test suite can be left in an inconsistent state that causes later tests to fail.
Cause and Diagnostic Table
| Symptom | Likely Cause | Why It Causes Flakiness |
|---|---|---|
| Passes in isolation, fails after a specific test | Fixture scope too broad (e.g., session instead of function) | State created once is reused across tests; mutations accumulate. |
| Unexpected fixture runs on tests that do not request it | Implicit autouse=True or plugin‑loaded fixtures | Fixture executes for every test in its scope, potentially altering global state. |
| Teardown errors hidden; later tests see dirty resources | Teardown raises an exception and pytest suppresses it, skipping remaining finalizers | Cleanup is incomplete; resources remain allocated for the next test. |
| Cleanup runs in the wrong order | Finalizer order is reversed relative to setup order | Dependent resources are torn down before the resources that depend on them. |
Ordered Checks
Confirm order dependency
Run the suspected test and its predecessor with
--setup-showto see the exact setup/teardown sequence.pytest -k predecessor_test failing_test --setup-showExpected: the output shows
SETUPandTEARDOWNlines for each fixture. Compare with running the failing test alone.Surface hidden teardown failures
Use the report option
-r ato reveal any teardown failures that pytest otherwise suppresses.pytest -r aIf teardown errors appear, they will be listed in the final summary.
Inspect fixture definitions
Search for
@pytest.fixturedeclarations inconftest.pyand test modules. Verify thescopeattribute and whetherautouse=Trueis set.grep -R "@pytest.fixture" conftest.py tests/Expected: session‑scoped fixtures should be immutable or explicitly reset.
Verify collection scope
Run
pytest --collect-onlyto confirm how pytest associates fixtures with tests and their declared scopes.pytest --collect-only -qIsolate by temporary scoping
Temporarily change a suspect fixture’s scope to
functionin a feature branch and re‑run the subset of failing tests.@pytest.fixture(scope="function") def temp_workdir(tmp_path): d = tmp_path / "work" d.mkdir() yield d # cleanup is per testNote: this increases runtime; measure the impact before merging.
Fixes Tied to Findings
Narrow scope where isolation is required
Mutable state (temporary directories, database connections, file handles) should use
functionorclassscope. Immutable resources (e.g., configuration data) can remainsessionscoped.Make autouse explicit
Remove
autouse=Trueunless the fixture’s side‑effects are truly global. Require the fixture explicitly in test signatures to make dependencies visible.@pytest.fixture def db_connection(): conn = connect() yield conn conn.close()Guard teardown and surface failures
Do not swallow exceptions in finalizers. Log the error and re‑raise so that pytest reports the failure.
@pytest.fixture def resource(): r = acquire() try: yield r finally: try: r.release() except Exception as e: # Log to stderr or a logger print(f"Teardown error: {e}", file=sys.stderr) raiseSplit combined fixtures
Separate setup and teardown responsibilities into distinct fixtures. This ensures that the finalizer order follows the reverse of the setup order.
Verification
- Run
pytest --collect-onlyand verify fixture scopes. - Execute the failing test in isolation and then as part of the suite to confirm that order dependency has been removed.
- Enable
-r aand--setup-showto confirm that teardown runs and any errors are reported.
Escalation Criteria
- Teardown failures persist after scoping changes and explicit error handling.
- External services (databases, containers) remain in an inconsistent state across workers.
- Flakiness only appears under parallel execution (e.g.,
pytest-xdist).
In those cases, consider process isolation with pytest-xdist --forked or running separate test sessions per resource type. Always validate the behavior in both local and CI environments, as plugin loading order can affect autouse fixtures.
Limitations
This guide focuses on well‑documented pytest fixture behavior in pytest 7.x and later. It does not cover custom plugin finalizer manipulation or non‑standard fixture implementations. Changing fixture scopes may increase test runtime; measure the impact before committing.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.