[{"data":1,"prerenderedAt":887},["ShallowReactive",2],{"page-\u002Fadvanced-pytest-architecture-configuration\u002Fmastering-pytest-fixtures\u002Ftaming-autouse-fixtures-in-large-suites\u002F":3},{"id":4,"title":5,"body":6,"description":850,"extension":851,"meta":852,"navigation":90,"path":883,"seo":884,"stem":885,"__hash__":886},"content\u002Fadvanced-pytest-architecture-configuration\u002Fmastering-pytest-fixtures\u002Ftaming-autouse-fixtures-in-large-suites\u002Findex.md","Taming Autouse Fixtures in Large Suites",{"type":7,"value":8,"toc":839},"minimark",[9,13,21,26,55,59,62,166,207,244,247,359,363,376,387,391,424,428,434,440,454,534,538,541,597,600,609,715,719,722,728,734,744,758,761,765,771,788,797,801,830,835],[10,11,12],"p",{},"An autouse fixture runs for every test in its scope without any test asking for it. That is precisely its value — a network block, a reset of a global registry, a leak check that no test should be able to forget — and precisely its danger. Autouse fixtures are invisible at the call site, so a test's behaviour depends on code the reader cannot see from the test, and a suite that accumulates them gradually turns into one where nobody can say what state a given test starts in.",[10,14,15,16,20],{},"The discipline that keeps them useful is short: autouse only for behaviour that is universal and invisible to test logic, placed in the narrowest ",[17,18,19],"code",{},"conftest.py"," that covers the tests needing it, cheap enough that paying for it everywhere is acceptable, and with a documented opt-out for the rare exception. This guide covers each of those, plus the audit that finds the autouse fixtures that should not be.",[22,23,25],"h2",{"id":24},"prerequisites","Prerequisites",[27,28,29,36,45],"ul",{},[30,31,32,35],"li",{},[17,33,34],{},"pytest >= 8.0",".",[30,37,38,39,44],{},"The conftest hierarchy rules from ",[40,41,43],"a",{"href":42},"\u002Fadvanced-pytest-architecture-configuration\u002Fmanaging-conftest-hierarchies\u002F","managing conftest hierarchies",", since where an autouse fixture lives decides which tests it affects.",[30,46,47,50,51,54],{},[17,48,49],{},"--fixtures-per-test"," and ",[17,52,53],{},"--setup-show",", both built into pytest, for auditing.",[22,56,58],{"id":57},"solution","Solution",[10,60,61],{},"Reserve autouse for universal guards, place them narrowly, and give each an explicit opt-out.",[63,64,69],"pre",{"className":65,"code":66,"language":67,"meta":68,"style":68},"language-python shiki shiki-themes github-light github-dark","# tests\u002Fconftest.py — universal and invisible: every test should have it.\nimport socket\n\nimport pytest\n\n\n@pytest.fixture(autouse=True)\ndef block_network(request, monkeypatch):\n    # Opt-out: tests that genuinely need the network say so explicitly.\n    if request.node.get_closest_marker(\"allow_network\"):\n        return\n\n    def guard(*args, **kwargs):\n        raise RuntimeError(f\"network access in {request.node.nodeid}\")\n\n    monkeypatch.setattr(socket.socket, \"connect\", guard)\n","python","",[17,70,71,79,85,92,98,103,108,114,120,126,132,138,143,149,155,160],{"__ignoreMap":68},[72,73,76],"span",{"class":74,"line":75},"line",1,[72,77,78],{},"# tests\u002Fconftest.py — universal and invisible: every test should have it.\n",[72,80,82],{"class":74,"line":81},2,[72,83,84],{},"import socket\n",[72,86,88],{"class":74,"line":87},3,[72,89,91],{"emptyLinePlaceholder":90},true,"\n",[72,93,95],{"class":74,"line":94},4,[72,96,97],{},"import pytest\n",[72,99,101],{"class":74,"line":100},5,[72,102,91],{"emptyLinePlaceholder":90},[72,104,106],{"class":74,"line":105},6,[72,107,91],{"emptyLinePlaceholder":90},[72,109,111],{"class":74,"line":110},7,[72,112,113],{},"@pytest.fixture(autouse=True)\n",[72,115,117],{"class":74,"line":116},8,[72,118,119],{},"def block_network(request, monkeypatch):\n",[72,121,123],{"class":74,"line":122},9,[72,124,125],{},"    # Opt-out: tests that genuinely need the network say so explicitly.\n",[72,127,129],{"class":74,"line":128},10,[72,130,131],{},"    if request.node.get_closest_marker(\"allow_network\"):\n",[72,133,135],{"class":74,"line":134},11,[72,136,137],{},"        return\n",[72,139,141],{"class":74,"line":140},12,[72,142,91],{"emptyLinePlaceholder":90},[72,144,146],{"class":74,"line":145},13,[72,147,148],{},"    def guard(*args, **kwargs):\n",[72,150,152],{"class":74,"line":151},14,[72,153,154],{},"        raise RuntimeError(f\"network access in {request.node.nodeid}\")\n",[72,156,158],{"class":74,"line":157},15,[72,159,91],{"emptyLinePlaceholder":90},[72,161,163],{"class":74,"line":162},16,[72,164,165],{},"    monkeypatch.setattr(socket.socket, \"connect\", guard)\n",[63,167,169],{"className":65,"code":168,"language":67,"meta":68,"style":68},"# tests\u002Fintegration\u002Fconftest.py — narrow: only integration tests need it.\nimport pytest\n\n\n@pytest.fixture(autouse=True)\ndef clean_message_bus(bus):\n    yield\n    bus.purge()          # every integration test leaves the bus empty\n",[17,170,171,176,180,184,188,192,197,202],{"__ignoreMap":68},[72,172,173],{"class":74,"line":75},[72,174,175],{},"# tests\u002Fintegration\u002Fconftest.py — narrow: only integration tests need it.\n",[72,177,178],{"class":74,"line":81},[72,179,97],{},[72,181,182],{"class":74,"line":87},[72,183,91],{"emptyLinePlaceholder":90},[72,185,186],{"class":74,"line":94},[72,187,91],{"emptyLinePlaceholder":90},[72,189,190],{"class":74,"line":100},[72,191,113],{},[72,193,194],{"class":74,"line":105},[72,195,196],{},"def clean_message_bus(bus):\n",[72,198,199],{"class":74,"line":110},[72,200,201],{},"    yield\n",[72,203,204],{"class":74,"line":116},[72,205,206],{},"    bus.purge()          # every integration test leaves the bus empty\n",[63,208,210],{"className":65,"code":209,"language":67,"meta":68,"style":68},"# A test that needs the exception declares it, visibly.\nimport pytest\n\n\n@pytest.mark.allow_network\ndef test_downloads_the_public_schema(http):\n    assert http.get(\"https:\u002F\u002Fschema.example\u002Fopenapi.json\").ok\n",[17,211,212,217,221,225,229,234,239],{"__ignoreMap":68},[72,213,214],{"class":74,"line":75},[72,215,216],{},"# A test that needs the exception declares it, visibly.\n",[72,218,219],{"class":74,"line":81},[72,220,97],{},[72,222,223],{"class":74,"line":87},[72,224,91],{"emptyLinePlaceholder":90},[72,226,227],{"class":74,"line":94},[72,228,91],{"emptyLinePlaceholder":90},[72,230,231],{"class":74,"line":100},[72,232,233],{},"@pytest.mark.allow_network\n",[72,235,236],{"class":74,"line":105},[72,237,238],{},"def test_downloads_the_public_schema(http):\n",[72,240,241],{"class":74,"line":110},[72,242,243],{},"    assert http.get(\"https:\u002F\u002Fschema.example\u002Fopenapi.json\").ok\n",[10,245,246],{},"The first fixture is a guard every test in the suite should have, and its opt-out is a marker that appears on the test — so the exception is visible exactly where it applies. The second lives in the integration subtree, so unit tests never pay for a message bus they do not use.",[248,249,252,355],"figure",{"className":250},[251],"diagram",[253,254,261,262,261,266,261,270,261,278,261,288,261,297,261,303,261,308,261,315,261,320,261,325,261,329,261,334,261,340,261,344,261,348,261,351],"svg",{"viewBox":255,"role":256,"ariaLabelledBy":257,"xmlns":260},"0 0 820 268","img",[258,259],"au-t","au-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[263,264,265],"title",{"id":258},"Autouse fixtures and the tests they reach",[267,268,269],"desc",{"id":259},"A directory tree. An autouse network block in the root conftest reaches every test in the suite. An autouse bus purge in the integration conftest reaches only integration tests. A single test marked allow_network opts out of the root guard, and the marker is visible on that test.",[271,272],"rect",{"x":273,"y":273,"width":274,"height":275,"rx":276,"fill":277},"0","820","268","14","#fffdf8",[279,280,287],"text",{"x":281,"y":282,"textAnchor":283,"fontSize":284,"fontWeight":285,"fill":286},"410","28","middle","16","700","#3d405b","Reach is decided by where the fixture lives",[271,289],{"x":290,"y":291,"width":292,"height":291,"rx":293,"fill":294,"stroke":295,"strokeWidth":296},"26","50","768","11","#f7f0da","#f2cc8f","2",[279,298,302],{"x":299,"y":300,"fontSize":301,"fontWeight":285,"fill":286},"46","72","12","tests\u002Fconftest.py · autouse block_network",[279,304,307],{"x":299,"y":305,"fontSize":293,"fill":306},"90","#8a5a00","reaches every test in the suite",[271,309],{"x":299,"y":310,"width":311,"height":312,"rx":293,"fill":313,"stroke":314,"strokeWidth":296},"112","360","130","#e6f0ea","#81b29a",[279,316,319],{"x":317,"y":318,"textAnchor":283,"fontSize":301,"fontWeight":285,"fill":286},"226","136","tests\u002Funit\u002F",[279,321,324],{"x":322,"y":323,"fontSize":293,"fill":286},"64","162","network blocked",[279,326,328],{"x":322,"y":327,"fontSize":293,"fill":286},"184","no bus fixture — never pays for it",[279,330,333],{"x":322,"y":331,"fontSize":293,"fill":332},"214","#2a5f49","fast and isolated",[271,335],{"x":336,"y":310,"width":337,"height":312,"rx":293,"fill":338,"stroke":286,"strokeWidth":339},"424","350","#f4f1de","1.6",[279,341,343],{"x":342,"y":318,"textAnchor":283,"fontSize":301,"fontWeight":285,"fill":286},"599","tests\u002Fintegration\u002F",[279,345,347],{"x":346,"y":323,"fontSize":293,"fill":286},"442","+ autouse clean_message_bus",[279,349,350],{"x":346,"y":327,"fontSize":293,"fill":286},"network blocked unless marked",[279,352,354],{"x":346,"y":331,"fontSize":293,"fill":353},"#8f3d22","@allow_network — visible on the test",[356,357,358],"figcaption",{},"The narrower the directory, the fewer tests pay for the fixture and the fewer readers are surprised by it.",[22,360,362],{"id":361},"why-this-works","Why this works",[10,364,365,366,368,369,371,372,375],{},"pytest collects autouse fixtures from every ",[17,367,19],{}," between the rootdir and a test's directory, and activates each one for every test in that subtree. The fixture's scope and its location therefore together decide its reach: a function-scoped autouse fixture in the root ",[17,370,19],{}," runs before every single test in the suite, while the same fixture in ",[17,373,374],{},"tests\u002Fintegration\u002Fconftest.py"," runs only for integration tests.",[10,377,378,379,382,383,386],{},"The marker-based opt-out works because ",[17,380,381],{},"request.node"," is the test item, and ",[17,384,385],{},"get_closest_marker"," finds a marker on the test, its class or its module. The fixture can therefore make its behaviour conditional on something the test declares, which is the only way a test can influence a fixture it did not request — and it keeps the exception visible in the test's own source.",[22,388,390],{"id":389},"edge-cases-and-failure-modes","Edge cases and failure modes",[27,392,393,400,406,412,418],{},[30,394,395,399],{},[396,397,398],"strong",{},"Autouse fixtures that do I\u002FO."," A database reset that takes 20 ms, autouse at the root, adds 20 seconds to a thousand-test suite — including unit tests that never touch the database. Move it to the subtree that needs it.",[30,401,402,405],{},[396,403,404],{},"Autouse fixtures that return values."," No test requested it, so no test can use the value. If tests need the value, the fixture should not be autouse.",[30,407,408,411],{},[396,409,410],{},"Order dependence between autouse fixtures."," Autouse fixtures in the same file run in definition order; across files, root first. Relying on that order is fragile; make one depend on the other explicitly.",[30,413,414,417],{},[396,415,416],{},"Session-scoped autouse fixtures."," They run once, before the first test in scope, which may be much later than expected when running a subset. Do not rely on them for side effects a test needs.",[30,419,420,423],{},[396,421,422],{},"Opt-outs used widely."," If a third of the tests carry the opt-out marker, the behaviour is not universal and should become an explicit fixture.",[22,425,427],{"id":426},"the-cost-that-autouse-hides","The cost that autouse hides",[10,429,430,431,433],{},"The most common problem with autouse fixtures is not correctness but cost, and it is invisible precisely because nobody requested the fixture. A single function-scoped autouse fixture in the root ",[17,432,19],{}," that takes fifteen milliseconds — a database truncate, a cache flush, a temporary directory copy — costs fifteen seconds on a thousand-test suite, and it costs the unit tests that never touched the database exactly as much as the integration tests that did.",[10,435,436,439],{},[17,437,438],{},"--durations"," does not attribute that time clearly, because setup time is reported per test and every test's setup is fifteen milliseconds slower. The suite simply looks uniformly slow, and the usual response is to parallelise it rather than to notice that a third of its runtime is one fixture running where it is not needed.",[10,441,442,443,446,447,449,450,453],{},"The diagnostic is to time the suite with the fixture temporarily disabled. Adding the opt-out marker to the whole unit subtree through a ",[17,444,445],{},"pytestmark"," in its ",[17,448,19],{},", or commenting out ",[17,451,452],{},"autouse=True"," for one run, gives a before-and-after figure in a minute. If the unit suite drops from forty seconds to twenty-five, the fixture belongs in the integration subtree, and moving it is a two-line change that gives every developer those fifteen seconds back on every run for the life of the project.",[248,455,457,528],{"className":456},[251],[253,458,261,463,261,466,261,469,261,473,261,478,261,483,261,489,261,494,261,498,261,502,261,506,261,511,261,515,261,517,261,519,261,523],{"viewBox":459,"role":256,"ariaLabelledBy":460,"xmlns":260},"0 0 800 234",[461,462],"cost-t","cost-d",[263,464,465],{"id":461},"Where an autouse fixture's cost lands",[267,467,468],{"id":462},"Two bars for a thousand-test suite. With a fifteen-millisecond autouse fixture at the root, every test pays, adding fifteen seconds including to unit tests that never use the resource. With the same fixture moved to the integration subtree, only the integration tests pay and the unit suite regains the time.",[271,470],{"x":273,"y":273,"width":471,"height":472,"rx":276,"fill":277},"800","234",[279,474,477],{"x":475,"y":282,"textAnchor":283,"fontSize":476,"fontWeight":285,"fill":286},"400","15.5","Invisible fixtures have visible costs",[279,479,482],{"x":480,"y":481,"fontSize":301,"fontWeight":285,"fill":353},"34","80","autouse at the root",[271,484],{"x":485,"y":486,"width":487,"height":282,"rx":488,"fill":313,"stroke":314,"strokeWidth":339},"220","62","380","7",[279,490,493],{"x":281,"y":491,"textAnchor":283,"fontSize":492,"fill":286},"81","10.5","unit tests — 700 × 15 ms = 10.5 s wasted",[271,495],{"x":496,"y":486,"width":497,"height":282,"rx":488,"fill":294,"stroke":295,"strokeWidth":339},"604","160",[279,499,501],{"x":500,"y":491,"textAnchor":283,"fontSize":492,"fill":286},"684","integration 4.5 s",[279,503,505],{"x":480,"y":504,"fontSize":301,"fontWeight":285,"fill":332},"150","autouse in integration\u002F",[271,507],{"x":485,"y":508,"width":487,"height":282,"rx":488,"fill":277,"stroke":509,"strokeWidth":510},"132","rgba(61,64,91,0.35)","1.4",[279,512,514],{"x":281,"y":513,"textAnchor":283,"fontSize":492,"fill":286},"151","unit tests — no cost",[271,516],{"x":496,"y":508,"width":497,"height":282,"rx":488,"fill":294,"stroke":295,"strokeWidth":339},[279,518,501],{"x":500,"y":513,"textAnchor":283,"fontSize":492,"fill":286},[271,520],{"x":480,"y":327,"width":521,"height":480,"rx":522,"fill":277,"stroke":509,"strokeWidth":510},"730","9",[279,524,527],{"x":475,"y":525,"textAnchor":283,"fontSize":526,"fill":286},"206","11.5","Same fixture, same integration cost — ten seconds returned to every unit run by moving one file's worth of code.",[356,529,530,531,533],{},"A before-and-after timing with the fixture disabled for one run is the quickest way to see this; ",[17,532,438],{}," spreads it too thinly to notice.",[22,535,537],{"id":536},"auditing-an-existing-suite","Auditing an existing suite",[10,539,540],{},"Large suites accumulate autouse fixtures the way codebases accumulate global variables — one at a time, each individually reasonable. A periodic audit finds the ones that no longer earn their invisibility.",[63,542,546],{"className":543,"code":544,"language":545,"meta":68,"style":68},"language-bash shiki shiki-themes github-light github-dark","# Every autouse fixture in the suite, with its location.\ngrep -rn \"autouse=True\" tests\u002F --include=conftest.py\n\n# What a specific test actually gets, including autouse fixtures.\npytest tests\u002Funit\u002Ftest_parser.py::test_empty_input --fixtures-per-test -q\n","bash",[17,547,548,554,574,578,583],{"__ignoreMap":68},[72,549,550],{"class":74,"line":75},[72,551,553],{"class":552},"sJ8bj","# Every autouse fixture in the suite, with its location.\n",[72,555,556,560,564,568,571],{"class":74,"line":81},[72,557,559],{"class":558},"sScJk","grep",[72,561,563],{"class":562},"sj4cs"," -rn",[72,565,567],{"class":566},"sZZnC"," \"autouse=True\"",[72,569,570],{"class":566}," tests\u002F",[72,572,573],{"class":562}," --include=conftest.py\n",[72,575,576],{"class":74,"line":87},[72,577,91],{"emptyLinePlaceholder":90},[72,579,580],{"class":74,"line":94},[72,581,582],{"class":552},"# What a specific test actually gets, including autouse fixtures.\n",[72,584,585,588,591,594],{"class":74,"line":100},[72,586,587],{"class":558},"pytest",[72,589,590],{"class":566}," tests\u002Funit\u002Ftest_parser.py::test_empty_input",[72,592,593],{"class":562}," --fixtures-per-test",[72,595,596],{"class":562}," -q\n",[10,598,599],{},"For each autouse fixture found, ask three questions. Does every test beneath it genuinely need the behaviour, or only some? Is it cheap enough that every test paying for it is acceptable? Would a reader of a failing test be surprised to learn it was running? A \"no\", \"no\" or \"yes\" respectively means it should become an explicit fixture that tests request by name.",[10,601,602,603,605,606,608],{},"The conversion is mechanical: remove ",[17,604,452],{},", and add the fixture name to the parameter list of the tests that need it. The collection-time ",[17,607,49],{}," output before and after confirms nothing else changed, and the tests that now name the fixture document their dependency where a reader will see it.",[248,610,612,712],{"className":611},[251],[253,613,261,618,261,621,261,624,261,639,261,642,261,645,261,648,261,652,261,656,261,662,261,665,261,669,261,672,261,676,261,680,261,684,261,687,261,692,261,696,261,700,261,705,261,709],{"viewBox":614,"role":256,"ariaLabelledBy":615,"xmlns":260},"0 0 800 236",[616,617],"aud-t","aud-d",[263,619,620],{"id":616},"Three questions for every autouse fixture",[267,622,623],{"id":617},"A decision sequence for each autouse fixture. If not every test beneath it needs the behaviour, or if it is too expensive to pay for everywhere, or if a reader would be surprised it ran, convert it to an explicit fixture. Only fixtures passing all three stay autouse.",[625,626,627,628,261],"defs",{},"\n    ",[629,630,635],"marker",{"id":631,"viewBox":632,"refX":522,"refY":633,"markerWidth":488,"markerHeight":488,"orient":634},"aud-a","0 0 10 10","5","auto-start-reverse",[636,637],"path",{"d":638,"fill":286},"M0 0 L10 5 L0 10 z",[271,640],{"x":273,"y":273,"width":471,"height":641,"rx":276,"fill":277},"236",[279,643,644],{"x":475,"y":282,"textAnchor":283,"fontSize":476,"fontWeight":285,"fill":286},"Stay autouse only if all three hold",[271,646],{"x":290,"y":647,"width":485,"height":481,"rx":293,"fill":338,"stroke":286,"strokeWidth":339},"56",[279,649,651],{"x":318,"y":650,"textAnchor":283,"fontSize":526,"fontWeight":285,"fill":286},"84","every test needs it?",[279,653,655],{"x":318,"y":654,"textAnchor":283,"fontSize":293,"fill":286},"106","not just most",[74,657],{"x1":658,"y1":659,"x2":660,"y2":659,"stroke":286,"strokeWidth":339,"markerEnd":661},"250","96","286","url(#aud-a)",[271,663],{"x":664,"y":647,"width":485,"height":481,"rx":293,"fill":338,"stroke":286,"strokeWidth":339},"292",[279,666,668],{"x":667,"y":650,"textAnchor":283,"fontSize":526,"fontWeight":285,"fill":286},"402","cheap everywhere?",[279,670,671],{"x":667,"y":654,"textAnchor":283,"fontSize":293,"fill":286},"no I\u002FO, no heavy setup",[74,673],{"x1":674,"y1":659,"x2":675,"y2":659,"stroke":286,"strokeWidth":339,"markerEnd":661},"516","552",[271,677],{"x":678,"y":647,"width":679,"height":481,"rx":293,"fill":338,"stroke":286,"strokeWidth":339},"558","216",[279,681,683],{"x":682,"y":650,"textAnchor":283,"fontSize":526,"fontWeight":285,"fill":286},"666","unsurprising?",[279,685,686],{"x":682,"y":654,"textAnchor":283,"fontSize":293,"fill":286},"invisible to test logic",[271,688],{"x":290,"y":689,"width":311,"height":690,"rx":691,"fill":313,"stroke":314,"strokeWidth":296},"156","60","10",[279,693,695],{"x":525,"y":694,"textAnchor":283,"fontSize":301,"fontWeight":285,"fill":286},"182","all yes → keep autouse",[279,697,699],{"x":525,"y":698,"textAnchor":283,"fontSize":293,"fill":332},"202","network block, leak checks, seed resets",[271,701],{"x":702,"y":689,"width":311,"height":690,"rx":691,"fill":703,"stroke":704,"strokeWidth":296},"414","#fbe9e3","#e07a5f",[279,706,708],{"x":707,"y":694,"textAnchor":283,"fontSize":301,"fontWeight":285,"fill":286},"594","any no → explicit fixture",[279,710,711],{"x":707,"y":698,"textAnchor":283,"fontSize":293,"fill":353},"tests request it by name",[356,713,714],{},"The surviving autouse fixtures in a healthy suite tend to be guards — they prevent things rather than provide things.",[22,716,718],{"id":717},"good-autouse-fixtures-worth-copying","Good autouse fixtures worth copying",[10,720,721],{},"It helps to have concrete examples of fixtures that pass all three audit questions, because they share a shape that is easy to recognise once seen.",[10,723,724,727],{},[396,725,726],{},"The network guard"," shown above prevents every test from reaching the internet by accident. No test's logic depends on it, every test benefits, and it costs a single attribute patch.",[10,729,730,733],{},[396,731,732],{},"The leak check"," asserts at teardown that no asyncio tasks, threads or open file descriptors survived the test. It prevents a whole class of order-dependent failures, and the only tests that notice it are the ones that were leaking.",[10,735,736,739,740,743],{},[396,737,738],{},"The seed reset"," sets ",[17,741,742],{},"random.seed"," and NumPy's generator to a value derived from the test's node id before each test. It makes every test reproducible in isolation, and no test's assertion depends on which seed was chosen.",[10,745,746,749,750,753,754,757],{},[396,747,748],{},"The environment snapshot"," records ",[17,751,752],{},"os.environ"," before each test and restores it after, so a test that sets a variable cannot leak it into the next. ",[17,755,756],{},"monkeypatch.setenv"," already does this for variables set through it; the snapshot catches the ones set directly.",[10,759,760],{},"All four are guards: they prevent something rather than provide something, they are cheap, and a reader of any test in the suite can safely ignore them. That is the profile of an autouse fixture that will still be a good idea in three years, and anything that does not fit it is better off as a fixture tests request by name.",[22,762,764],{"id":763},"frequently-asked-questions","Frequently Asked Questions",[10,766,767,770],{},[396,768,769],{},"When is an autouse fixture the right choice?","\nWhen the behaviour is genuinely universal and invisible to test logic: blocking network access, resetting a global registry, freezing a random seed, asserting no tasks leaked. If a test could reasonably want the opposite behaviour, it should be an explicit fixture the test requests.",[10,772,773,776,777,780,781,784,785,787],{},[396,774,775],{},"How do I find which autouse fixtures apply to a test?","\nRun ",[17,778,779],{},"pytest --setup-show"," on that test, or ",[17,782,783],{},"pytest --fixtures-per-test",", which lists every fixture used by each test including autouse ones and where they are defined. Autouse fixtures in a parent ",[17,786,19],{}," apply to every test beneath it.",[10,789,790,793,794,796],{},[396,791,792],{},"Can a test opt out of an autouse fixture?","\nNot directly. The fixture can check for a marker on ",[17,795,381],{}," and skip its behaviour, which is the standard pattern for opting out. If many tests need to opt out, the fixture should not be autouse.",[22,798,800],{"id":799},"related","Related",[27,802,803,810,816,823],{},[30,804,805,809],{},[40,806,808],{"href":807},"\u002Fadvanced-pytest-architecture-configuration\u002Fmastering-pytest-fixtures\u002F","Mastering pytest Fixtures"," — scope and dependency rules that apply to autouse fixtures too.",[30,811,812,815],{},[40,813,814],{"href":42},"Managing conftest Hierarchies"," — choosing the narrowest directory for a fixture.",[30,817,818,822],{},[40,819,821],{"href":820},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fmocking-network-and-http-calls\u002Fblocking-accidental-network-calls-in-pytest\u002F","Blocking Accidental Network Calls in pytest"," — the canonical good autouse fixture, in full.",[30,824,825,829],{},[40,826,828],{"href":827},"\u002Fadvanced-pytest-architecture-configuration\u002Fpytest-configuration-best-practices\u002Fpytest-markers-for-conditional-test-execution\u002F","pytest Markers for Conditional Test Execution"," — registering the opt-out markers strictly.",[10,831,832,833],{},"← Back to ",[40,834,808],{"href":807},[836,837,838],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":68,"searchDepth":81,"depth":81,"links":840},[841,842,843,844,845,846,847,848,849],{"id":24,"depth":81,"text":25},{"id":57,"depth":81,"text":58},{"id":361,"depth":81,"text":362},{"id":389,"depth":81,"text":390},{"id":426,"depth":81,"text":427},{"id":536,"depth":81,"text":537},{"id":717,"depth":81,"text":718},{"id":763,"depth":81,"text":764},{"id":799,"depth":81,"text":800},"Keep autouse fixtures from becoming hidden global state: where they belong, what they may do, how to audit them, and how to replace the ones that couple unrelated tests.","md",{"slug":853,"type":854,"breadcrumb":855,"datePublished":856,"dateModified":856,"faq":857,"howto":864},"taming-autouse-fixtures-in-large-suites","article","Autouse Fixtures","2026-09-18",[858,860,862],{"q":769,"a":859},"When the behaviour is genuinely universal and invisible to test logic: blocking network access, resetting a global registry, freezing a random seed, asserting no tasks leaked. If a test could reasonably want the opposite behaviour, it should be an explicit fixture the test requests.",{"q":775,"a":861},"Run pytest --setup-show on that test, or pytest --fixtures-per-test, which lists every fixture used by each test including autouse ones and where they are defined. Autouse fixtures in a parent conftest.py apply to every test beneath it.",{"q":792,"a":863},"Not directly. The fixture can check for a marker on request.node and skip its behaviour, which is the standard pattern for opting out. If many tests need to opt out, the fixture should not be autouse.",{"name":865,"description":866,"steps":867},"How to keep autouse fixtures under control","Restrict autouse to universal, invisible behaviour, place it at the narrowest directory, give it an opt-out marker, and audit periodically.",[868,871,874,877,880],{"name":869,"text":870},"Apply the universality test","Make a fixture autouse only if every test beneath it should have the behaviour and none should need the opposite.",{"name":872,"text":873},"Place it at the narrowest directory","Put the autouse fixture in the conftest.py of the subtree that needs it rather than the root.",{"name":875,"text":876},"Provide an opt-out marker","Check request.node.get_closest_marker in the fixture so exceptional tests can declare themselves.",{"name":878,"text":879},"Keep autouse fixtures cheap","Avoid I\u002FO and expensive setup in autouse fixtures, since their cost is paid by every test in scope.",{"name":881,"text":882},"Audit with fixtures-per-test","List autouse fixtures across the suite periodically and convert any that only some tests need.","\u002Fadvanced-pytest-architecture-configuration\u002Fmastering-pytest-fixtures\u002Ftaming-autouse-fixtures-in-large-suites",{"title":5,"description":850},"advanced-pytest-architecture-configuration\u002Fmastering-pytest-fixtures\u002Ftaming-autouse-fixtures-in-large-suites\u002Findex","b1mp4_jG_ssL1OIPR9A_FNZbdQXEVDQ2Txc2GnDEKFE",1789718767397]