For AI agentsA Markdown index of these docs is at /llms.txt. Every page is also Markdown: add .md to its address.

Agent Rules / Frameworks

Why does my agent break pytest fixtures it can't see?

Rule AR-34

Keep fixtures in the nearest conftest.py, name them for what they return, and don't use autouse to change behaviour.

pytest connects a test to its fixtures by parameter name, and a fixture can come from the test's module, any conftest.py above it, or a plugin. A test that names fake_charge in its signature shows the dependency. A test that gets a patched gateway from an autouse fixture shows nothing, so an agent reading or editing the test can't see what the test runs against. Put the fixture where the tests that use it live, name it for its value, and ask for it in the signature.

Why it happens

Fixtures are one of pytest's best features, and the docs describe the mechanism clearly. "At a basic level, test functions request fixtures they require by declaring them as arguments", and pytest searches for fixtures with the same names. A fixture "can also request any other fixture, no matter where it's defined, so long as the test requesting them can see all fixtures involved", and conftest.py "serves as a means of providing fixtures for an entire directory." Setup and teardown stay together with yield, and the default scope is function, "destroyed at the end of the test."

Four properties make the pattern hard to follow from a test file alone:

  • Lookup is by name, and the nearest definition wins. The how-to guide shows a fixture with the same name overridden "for a certain test directory level" and "for a certain test module". An agent that edits the fixture in the root conftest.py can change nothing for the tests under a subdirectory that overrides it, and an agent that adds a same-named fixture in a module changes it for that module only. A search for the name finds several definitions and no signal which one a test gets.
  • Autouse applies without a request. The docs: "Autouse fixtures are a convenient way to make all tests automatically request them", and "Because it happens automatically, both tests are affected by it, even though neither test requested it." The reference adds that autouse fixtures "are executed before other fixtures in that scope". A fixture that returns nothing and changes behaviour is a hidden setup step for every test in its reach. CH-02 walks through an autouse patch that outlived the refactor it was written for.
  • The fixture name is a string-like key. The test's parameter fake_charge and the fixture def fake_charge are linked only by pytest. A rename tool that sees them as unrelated variables can rename one and not the other, and the error appears as a missing fixture when the test runs.
  • Scope changes sharing. With a wider scope (module, package, session) a fixture's value is shared across tests. Dynamic scope is allowed: the callable "will be executed only once - during the fixture definition". An agent that changes scope="function" to "session" to speed up a run turns each test's data into the previous test's data.

Bad and good

Tests for a checkout function need a fake payment gateway and a clean database.

python# bad: tests/conftest.py import pytest from payments import gateway @pytest.fixture(autouse=True) def setup(monkeypatch, db): monkeypatch.setattr(gateway, "charge", lambda *a, **k: gateway.ChargeResult(ok=True, charge_id="ch_test")) db.reset() yield db.close()
python# bad: tests/test_checkout.py def test_checkout_returns_receipt(): receipt = checkout("cus_42", [1200, 800]) assert receipt.charge_id == "ch_test" # where does ch_test come from?

The fixture is named for nothing, it does two jobs, and it patches the gateway for every test in the directory, including those that never charge. The test's assertion depends on a value defined in another file.

python# good: tests/conftest.py from collections.abc import Iterator import pytest from tests.fakes import FakeCharge @pytest.fixture def database(db) -> Iterator[Database]: db.reset() yield db db.close() @pytest.fixture def fake_charge(monkeypatch: pytest.MonkeyPatch) -> FakeCharge: fake = FakeCharge() monkeypatch.setattr("payments.checkout.charge", fake) return fake
python# good: tests/test_checkout.py def test_checkout_charges_the_cart_total(fake_charge: FakeCharge, database: Database): receipt = checkout("cus_42", [1200, 800]) assert fake_charge.calls == [("cus_42", 2000, "usd")] assert receipt.charge_id == fake_charge.result.charge_id

Each fixture does one thing and is named for what the test receives. The test lists what it uses, and the assertion refers to the fake's own result. Patch placement ("payments.checkout.charge") is AR-35's subject.

Trade-offs and when autouse is fine

  • Autouse removes repetition. In a suite where every test needs the same thing, it is less typing. The cost is that the dependency is hidden. Request the fixture by name everywhere it matters, and keep a short list of autouse fixtures that change nothing observable.
  • Autouse for isolation, not behaviour. A fixture that resets a cache, seeds a random generator or creates a temporary working directory leaves test results the same. Document these in the conftest.py that holds them. One that replaces a function, changes a setting or fakes a service changes what the test means. Make that one explicit.
  • **usefixtures is the explicit middle.** The docs: "Sometimes test functions do not directly need access to a fixture object." @pytest.mark.usefixtures("cleandir") on a test, class or module shows the dependency without a parameter. Ruff's PT019 recommends it when a test takes a fixture only for side effects: "to make the dependency explicit".
  • **Nearest conftest.py over root.** A fixture only one directory uses belongs in that directory's conftest.py. The root file should hold fixtures that the whole suite shares. Smaller scopes mean fewer places to search.
  • Wider scopes are a speed trade. A session fixture saves setup time and shares state. Use it for read-only resources and keep mutable data at function scope.
  • Naming for the value costs a few characters. fake_charge over payments or setup. A reader and an agent can then match the parameter name to the fixture.

Enforce it

ToolSettingWhat it catchesChecked
ruffPT019 pytest-fixture-param-without-value (select the PT family)a test that takes a fixture only for its side effects; suggests @pytest.mark.usefixtures2026-10-30
ruffPT003 pytest-extraneous-scope-functionscope="function", which is the default and can be omitted2026-10-30
ruffPT001, PT002 (pytest-fixture-incorrect-parentheses-style, pytest-fixture-positional-args)fixture decorator style ("If a @pytest.fixture() doesn't take any arguments, the parentheses are optional") and pytest.fixture calls with positional argumentshttps://docs.astral.sh/ruff/rules/pytest-fixture-incorrect-parentheses-style/ and https://docs.astral.sh/ruff/rules/pytest-fixture-positional-args/ ; neither in ruff's default set
ruffPT022 pytest-useless-yield-fixture, PT021 pytest-fixture-finalizer-callback"unnecessary yield expressions in pytest fixtures"; "unnecessary request.addfinalizer usages in pytest fixtures"https://docs.astral.sh/ruff/rules/pytest-useless-yield-fixture/ and https://docs.astral.sh/ruff/rules/pytest-fixture-finalizer-callback/ ; neither in ruff's default set
ruffPT025, PT026 (pytest-erroneous-use-fixtures-on-fixture, pytest-use-fixtures-without-parameters)usefixtures used on a fixture function (the docs say it "cannot be used on fixture functions"), and used with no nameshttps://docs.astral.sh/ruff/rules/pytest-erroneous-use-fixtures-on-fixture/ and https://docs.astral.sh/ruff/rules/pytest-use-fixtures-without-parameters/ ; both are in ruff's default rule set (https://docs.astral.sh/ruff/default-rules/)
pytestpytest --fixtures"Show available fixtures, sorted by plugin appearance. Fixtures with leading _ are only shown with --verbose." (https://docs.pytest.org/en/stable/reference/reference.html); a test file or name can be passed (https://docs.pytest.org/en/stable/reference/fixtures.html).2026-10-30
pytestpytest --setup-plan"Show what fixtures and tests would be executed but don't execute anything." (https://docs.pytest.org/en/stable/reference/reference.html); the fixtures reference describes it as showing the order fixtures will execute in for a given test2026-10-30
pytest--setup-show, --fixtures-per-testboth exist: --setup-show "Show setup of fixtures while executing tests." and --fixtures-per-test "Show fixtures per test." (https://docs.pytest.org/en/stable/reference/reference.html)2026-10-30

No ruff rule in the index flags autouse=True. The rules PT004 and PT005 (fixture name underscore conventions) were removed in ruff 0.8.0, so don't cite them. The autouse limit is a review rule.

Rule block for AGENTS.md / CLAUDE.md

markdown## pytest fixtures - Put a fixture in the nearest conftest.py that covers every test using it. Fixtures used by one module live in that module. - Name a fixture for the value it returns (fake_charge, database, frozen_clock). Never setup, init or mock_stuff. - A fixture does one thing. Request it by name in the test's signature. - Don't use autouse=True for fixtures that replace functions, change settings or fake services. Use a named fixture, or @pytest.mark.usefixtures("name") when the test needs the effect and not the value. - Autouse is allowed only for isolation that changes no test result (temp dirs, cache resets), with a comment on the fixture. - Before renaming or changing a fixture, search for every definition with that name (pytest --fixtures shows the available fixtures) and every test that requests it. A same-named fixture in a nearer conftest.py or module overrides it. - Don't widen a fixture's scope to speed up tests unless its value is read-only.

Edge cases

  • Overriding by name. The override is documented behaviour. Mark an override with a comment naming what it replaces.
  • Fixtures requesting fixtures. Chains are normal: "The fixtures in pytest request fixtures just like tests." When changing a fixture's return type, check dependents, not only tests.
  • Parametrized fixtures. The docs: fixture functions can be parametrized, and each value is available as request.param. A parameter list in the decorator is a hidden cartesian product with the tests that use it.
  • Dynamic scope. A callable passed as scope is read once at definition. Don't compute it from state that changes.
  • **A project-wide usefixtures setting.** The docs mention that fixtures needed by all tests can be put "into a configuration file". The same hiding applies as with autouse. Treat it the same. The ini key is usefixtures ("List of fixtures that will be applied to all test functions"), shown in the how-to as usefixtures = ["cleandir"] under [pytest] in pytest.toml (https://docs.pytest.org/en/stable/reference/reference.html, https://docs.pytest.org/en/stable/how-to/fixtures.html).
  • Plugins. A fixture can come from an installed plugin, so a missing definition in the repo isn't proof of a missing fixture. pytest --fixtures lists those too.
  • Monkeypatch inside fixtures. Where to patch is AR-35. Production-code patching is AR-18.

References

pytest (fetched 2026-10-30):

  • Fixtures how-to (requesting by name, overriding, autouse, usefixtures, scope, teardown with yield, parametrized fixtures): https://docs.pytest.org/en/stable/how-to/fixtures.html
  • Fixtures reference (availability, conftest.py, autouse ordering): https://docs.pytest.org/en/stable/reference/fixtures.html
  • Monkeypatch how-to: https://docs.pytest.org/en/stable/how-to/monkeypatch.html

Ruff (fetched 2026-10-30):

  • PT019: https://docs.astral.sh/ruff/rules/pytest-fixture-param-without-value/
  • PT003: https://docs.astral.sh/ruff/rules/pytest-extraneous-scope-function/
  • PT024 (page opened, not used in the table): https://docs.astral.sh/ruff/rules/pytest-unnecessary-asyncio-mark-on-fixture/
  • Rule list (PT family, removed PT004 and PT005): https://docs.astral.sh/ruff/rules/

Chimera: CH-02.

Changelog

  • 2026-10-30: draft 1.
  • 2026-10-30: docs checks resolved
  • 2026-10-30: remaining checks closed (claims reduced to documented behaviour)
  • AR-18 Why does monkeypatching break agent edits?
  • AR-35 Where should I patch in pytest?
  • CH-02 Why do my pytest tests pass for a reason I can't see?

Chimeras that use this rule

  • CH-02 Why do my pytest tests pass for a reason I can't see?

Last verified

For agentsthis page as Markdown/agent-readable/ar-34.mdllms.txt