TL;DR
A test passes alone but fails in the full suite: another test pollutes shared state, database rows, module globals, environment variables, or files on disk, and the victim trips over it. Bisect by running halves of the suite together with the failing test until the polluter is isolated, or use pytest-randomly's seed to reproduce the exact order, then make the polluter clean up after itself.
The query
pytest AssertionError: assert 0 == 1 only when the full suite runs - test pollution how to find it
Use this when
- a test fails only when the full suite runs
- the failure depends on which tests run before it
- rerunning the suite shuffles the failure to a different victim
Not for
- the test fails alone too (that is a plain bug in the test or the code)
- the suite fails from resource exhaustion in full runs (memory or disk, not pollution)
- parallel-only failures (that is a shared-handle race, not ordering)
Steps
1. Confirm the test passes alone
Establish the baseline: the test in isolation is green, so the failure needs company.
pytest tests/test_billing.py::test_invoice_total -q
Expected output: the test passes alone, proving the failure is order-dependent.
2. Bisect the suite with the victim
Run the victim together with half the suite, then halve again. Each run tells you which half holds the polluter.
pytest tests/test_billing.py::test_invoice_total tests/test_a.py -q
pytest tests/test_billing.py::test_invoice_total tests/test_b.py -q
Expected output: each bisection step narrows the polluter to a smaller set of files, converging on one.
3. Reproduce with a fixed random order
pytest-randomly shuffles test order; its seed lets you replay the exact failing order and share it.
pip install pytest-randomly
pytest -p randomly -q 2>&1 | grep -i "using.*seed"
pytest -p randomly --randomly-seed=[seed value] tests/test_billing.py::test_invoice_total -q
Expected output: the failing order replays deterministically from the seed.
4. Fix the polluter's cleanup, not the victim
The victim is innocent. Find what the polluter leaves behind, a database row, a global, an env var, a file, and make it clean up in teardown or use a fixture.
@pytest.fixture
def invoice():
inv = create_invoice()
yield inv
delete_invoice(inv.id) # cleanup the polluter never had
Expected output: the full suite is green and the victim passes in any order.
Tool compatibility
- pytest 7 - 8
- pytest-randomly (for order reproduction)
- pytest-xdist (note: it changes grouping, which changes pollution patterns)
Variant phrasings
- AssertionError only when the full suite runs
- pytest test passes alone fails in suite
- test pollution how to find the polluter pytest
Why it happens
Tests share a process, and anything mutable and shared, database state, module-level globals, os.environ, the filesystem, leaked fixtures, is a channel for one test to affect another. Full suites run tests in an order no single test was written against, so pollution that is invisible in isolation becomes a failure. The polluter is usually a test that skips its teardown on failure, leaving state the next test inherits.
Edge cases / pitfalls
- the polluter is often a fixture, not a test: audit session- and module-scoped fixtures first
- database sequences and auto-increment ids are classic pollution vectors; reset them in fixtures
- xdist groups tests differently per worker, which can hide or reveal pollution; reproduce with the same -n value CI uses
- deleting the victim test to make the suite green is the cardinal sin of triage; it hides the polluter for the next victim
Provenance
Resolved from the public thread: https://vectle.com/posts/pstkGmThipjQyEmy5tnjin5A