[{"data":1,"prerenderedAt":1732},["ShallowReactive",2],{"page-\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-integration-with-pytest-and-frameworks\u002F":3},{"id":4,"title":5,"body":6,"description":1691,"extension":1692,"meta":1693,"navigation":304,"path":1728,"seo":1729,"stem":1730,"__hash__":1731},"content\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-integration-with-pytest-and-frameworks\u002Findex.md","Hypothesis Integration with pytest & Frameworks",{"type":7,"value":8,"toc":1669},"minimark",[9,13,18,62,66,77,80,268,272,277,382,392,396,472,521,532,536,614,617,621,682,703,707,761,764,768,771,814,821,852,881,887,890,894,1005,1009,1012,1015,1030,1044,1128,1132,1139,1156,1162,1181,1192,1196,1199,1216,1281,1288,1291,1295,1298,1325,1391,1394,1398,1401,1406,1415,1422,1426,1429,1439,1445,1451,1468,1537,1541,1544,1547,1556,1560,1569,1578,1591,1607,1620,1624,1659,1665],[10,11,12],"p",{},"Hypothesis is a pytest plugin, and most of the friction teams hit comes from the seam between the two: a fixture that runs once while the test body runs two hundred times, an example database that two parallel workers fight over, a failure in CI that nobody can reproduce because the seed was not recorded. None of these are subtle once the execution model is clear, and all of them are configuration rather than code.",[14,15,17],"h2",{"id":16},"prerequisites","Prerequisites",[19,20,21,33,43,55],"ul",{},[22,23,24,28,29,32],"li",{},[25,26,27],"code",{},"hypothesis >= 6.100"," and ",[25,30,31],{},"pytest >= 8.0",".",[22,34,35,38,39,42],{},[25,36,37],{},"hypothesis[django]"," if the Django integration is in use; ",[25,40,41],{},"pytest-xdist"," if the suite runs in parallel.",[22,44,45,46,49,50,32],{},"The ",[25,47,48],{},"@given"," and settings basics from ",[51,52,54],"a",{"href":53},"\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-framework-fundamentals\u002F","Hypothesis framework fundamentals",[22,56,57,58,32],{},"A transactional database fixture for the integration half — see ",[51,59,61],{"href":60},"\u002Fintegration-database-and-service-testing\u002Fdatabase-fixtures-and-transactional-tests\u002F","database fixtures and transactional tests",[14,63,65],{"id":64},"core-concept-one-test-item-many-executions","Core concept: one test item, many executions",[10,67,68,69,71,72,76],{},"pytest's unit of work is the test item. Hypothesis's unit of work is the example. A single ",[25,70,48],{}," test is ",[73,74,75],"em",{},"one"," item that pytest sets up once, tears down once, and reports once — while the function body runs once per generated example, potentially hundreds of times, inside that single setup.",[10,78,79],{},"Everything that goes wrong follows from that mismatch.",[81,82,85,264],"figure",{"className":83},[84],"diagram",[86,87,94,95,94,99,94,103,94,121,94,129,94,138,94,148,94,154,94,159,94,167,94,173,94,176,94,180,94,184,94,189,94,193,94,197,94,201,94,204,94,208,94,211,94,215,94,219,94,223,94,226,94,233,94,237,94,242,94,245,94,250,94,254,94,257,94,261],"svg",{"viewBox":88,"role":89,"ariaLabelledBy":90,"xmlns":93},"0 0 840 282","img",[91,92],"exec-t","exec-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[96,97,98],"title",{"id":91},"Fixture setup happens once; the test body runs many times",[100,101,102],"desc",{"id":92},"A timeline for one test item. Fixture setup runs once, then the function body executes for example one, example two and example three in sequence, each mutating the same fixture state, and finally teardown runs once. A note marks that the second and third examples see state left by the first.",[104,105,106,107,94],"defs",{},"\n    ",[108,109,116],"marker",{"id":110,"viewBox":111,"refX":112,"refY":113,"markerWidth":114,"markerHeight":114,"orient":115},"exec-a","0 0 10 10","9","5","7","auto-start-reverse",[117,118],"path",{"d":119,"fill":120},"M0 0 L10 5 L0 10 z","#3d405b",[122,123],"rect",{"x":124,"y":124,"width":125,"height":126,"rx":127,"fill":128},"0","840","282","14","#fffdf8",[130,131,137],"text",{"x":132,"y":133,"textAnchor":134,"fontSize":135,"fontWeight":136,"fill":120},"420","28","middle","16","700","One pytest item, two hundred executions",[122,139],{"x":140,"y":141,"width":142,"height":143,"rx":144,"fill":145,"stroke":146,"strokeWidth":147},"26","56","150","62","11","#f7f0da","#f2cc8f","2",[130,149,153],{"x":150,"y":151,"textAnchor":134,"fontSize":152,"fontWeight":136,"fill":120},"101","82","12","fixture setup",[130,155,158],{"x":150,"y":156,"textAnchor":134,"fontSize":144,"fill":157},"102","#8a5a00","runs once",[160,161],"line",{"x1":162,"y1":163,"x2":164,"y2":163,"stroke":120,"strokeWidth":165,"markerEnd":166},"180","87","208","1.5","url(#exec-a)",[122,168],{"x":169,"y":141,"width":170,"height":143,"rx":144,"fill":171,"stroke":172,"strokeWidth":147},"214","136","#e6f0ea","#81b29a",[130,174,175],{"x":126,"y":151,"textAnchor":134,"fontSize":152,"fontWeight":136,"fill":120},"example 1",[130,177,179],{"x":126,"y":156,"textAnchor":134,"fontSize":144,"fill":178},"#2a5f49","clean state",[160,181],{"x1":182,"y1":163,"x2":183,"y2":163,"stroke":120,"strokeWidth":165,"markerEnd":166},"354","382",[122,185],{"x":186,"y":141,"width":170,"height":143,"rx":144,"fill":187,"stroke":188,"strokeWidth":147},"388","#fbe9e3","#e07a5f",[130,190,192],{"x":191,"y":151,"textAnchor":134,"fontSize":152,"fontWeight":136,"fill":120},"456","example 2",[130,194,196],{"x":191,"y":156,"textAnchor":134,"fontSize":144,"fill":195},"#8f3d22","sees example 1",[160,198],{"x1":199,"y1":163,"x2":200,"y2":163,"stroke":120,"strokeWidth":165,"markerEnd":166},"528","556",[122,202],{"x":203,"y":141,"width":170,"height":143,"rx":144,"fill":187,"stroke":188,"strokeWidth":147},"562",[130,205,207],{"x":206,"y":151,"textAnchor":134,"fontSize":152,"fontWeight":136,"fill":120},"630","example 3…",[130,209,210],{"x":206,"y":156,"textAnchor":134,"fontSize":144,"fill":195},"and example 2",[160,212],{"x1":213,"y1":163,"x2":214,"y2":163,"stroke":120,"strokeWidth":165,"markerEnd":166},"702","730",[122,216],{"x":217,"y":141,"width":218,"height":143,"rx":144,"fill":145,"stroke":146,"strokeWidth":147},"734","80",[130,220,222],{"x":221,"y":151,"textAnchor":134,"fontSize":152,"fontWeight":136,"fill":120},"774","teardown",[130,224,225],{"x":221,"y":156,"textAnchor":134,"fontSize":144,"fill":157},"once",[122,227],{"x":140,"y":228,"width":229,"height":230,"rx":231,"fill":128,"stroke":188,"strokeWidth":232},"146","788","52","10","1.8",[130,234,236],{"x":132,"y":235,"textAnchor":134,"fontSize":152,"fontWeight":136,"fill":120},"168","HealthCheck.function_scoped_fixture fires here",[130,238,241],{"x":132,"y":239,"textAnchor":134,"fontSize":240,"fill":120},"188","11.5","not a false alarm: the later examples are running against dirty state",[122,243],{"x":140,"y":244,"width":186,"height":230,"rx":231,"fill":171,"stroke":172,"strokeWidth":232},"212",[130,246,249],{"x":247,"y":248,"textAnchor":134,"fontSize":240,"fontWeight":136,"fill":120},"220","234","fix A: set up inside the test body",[130,251,253],{"x":247,"y":252,"textAnchor":134,"fontSize":144,"fill":120},"253","per-example state, explicitly created",[122,255],{"x":256,"y":244,"width":186,"height":230,"rx":231,"fill":171,"stroke":172,"strokeWidth":232},"426",[130,258,260],{"x":259,"y":248,"textAnchor":134,"fontSize":240,"fontWeight":136,"fill":120},"620","fix B: widen the fixture, reset per example",[130,262,263],{"x":259,"y":252,"textAnchor":134,"fontSize":144,"fill":120},"expensive setup shared, state rolled back",[265,266,267],"figcaption",{},"The health check is not pedantry. A test whose examples share mutable state can pass because of an ordering accident and fail on a different seed.",[14,269,271],{"id":270},"step-by-step-implementation","Step-by-step implementation",[273,274,276],"h3",{"id":275},"_1-register-profiles-instead-of-scattering-settings","1. Register profiles instead of scattering settings",[278,279,284],"pre",{"className":280,"code":281,"language":282,"meta":283,"style":283},"language-python shiki shiki-themes github-light github-dark","# conftest.py\nimport os\n\nfrom hypothesis import HealthCheck, settings\n\nsettings.register_profile(\"dev\", max_examples=25, deadline=None)\nsettings.register_profile(\n    \"ci\",\n    max_examples=300,\n    deadline=1000,                       # ms; a slow example is a real signal\n    derandomize=False,                   # keep searching in CI\n    print_blob=True,                     # print @reproduce_failure on failure\n)\nsettings.register_profile(\"nightly\", max_examples=2000, deadline=None)\n\nsettings.load_profile(os.environ.get(\"HYPOTHESIS_PROFILE\", \"dev\"))\n","python","",[25,285,286,293,299,306,312,317,323,329,335,341,347,353,359,365,371,376],{"__ignoreMap":283},[287,288,290],"span",{"class":160,"line":289},1,[287,291,292],{},"# conftest.py\n",[287,294,296],{"class":160,"line":295},2,[287,297,298],{},"import os\n",[287,300,302],{"class":160,"line":301},3,[287,303,305],{"emptyLinePlaceholder":304},true,"\n",[287,307,309],{"class":160,"line":308},4,[287,310,311],{},"from hypothesis import HealthCheck, settings\n",[287,313,315],{"class":160,"line":314},5,[287,316,305],{"emptyLinePlaceholder":304},[287,318,320],{"class":160,"line":319},6,[287,321,322],{},"settings.register_profile(\"dev\", max_examples=25, deadline=None)\n",[287,324,326],{"class":160,"line":325},7,[287,327,328],{},"settings.register_profile(\n",[287,330,332],{"class":160,"line":331},8,[287,333,334],{},"    \"ci\",\n",[287,336,338],{"class":160,"line":337},9,[287,339,340],{},"    max_examples=300,\n",[287,342,344],{"class":160,"line":343},10,[287,345,346],{},"    deadline=1000,                       # ms; a slow example is a real signal\n",[287,348,350],{"class":160,"line":349},11,[287,351,352],{},"    derandomize=False,                   # keep searching in CI\n",[287,354,356],{"class":160,"line":355},12,[287,357,358],{},"    print_blob=True,                     # print @reproduce_failure on failure\n",[287,360,362],{"class":160,"line":361},13,[287,363,364],{},")\n",[287,366,368],{"class":160,"line":367},14,[287,369,370],{},"settings.register_profile(\"nightly\", max_examples=2000, deadline=None)\n",[287,372,374],{"class":160,"line":373},15,[287,375,305],{"emptyLinePlaceholder":304},[287,377,379],{"class":160,"line":378},16,[287,380,381],{},"settings.load_profile(os.environ.get(\"HYPOTHESIS_PROFILE\", \"dev\"))\n",[10,383,384,387,388,391],{},[25,385,386],{},"print_blob=True"," in CI is the single most useful setting here: on failure, Hypothesis prints a ",[25,389,390],{},"@reproduce_failure"," decorator that reproduces the exact example locally, without needing the example database.",[273,393,395],{"id":394},"_2-fix-the-fixture-scoping","2. Fix the fixture scoping",[278,397,399],{"className":280,"code":398,"language":282,"meta":283,"style":283},"import pytest\nfrom hypothesis import given, strategies as st\n\n\n# WRONG: cart is created once, mutated by every example.\n@pytest.fixture\ndef cart():\n    return Cart()\n\n\n@given(st.lists(st.integers(min_value=1, max_value=100)))\ndef test_total_is_the_sum(cart, prices):      # health check fires\n    for price in prices:\n        cart.add(price)\n    assert cart.total() == sum(prices)        # fails from example two onward\n",[25,400,401,406,411,415,419,424,429,434,439,443,447,452,457,462,467],{"__ignoreMap":283},[287,402,403],{"class":160,"line":289},[287,404,405],{},"import pytest\n",[287,407,408],{"class":160,"line":295},[287,409,410],{},"from hypothesis import given, strategies as st\n",[287,412,413],{"class":160,"line":301},[287,414,305],{"emptyLinePlaceholder":304},[287,416,417],{"class":160,"line":308},[287,418,305],{"emptyLinePlaceholder":304},[287,420,421],{"class":160,"line":314},[287,422,423],{},"# WRONG: cart is created once, mutated by every example.\n",[287,425,426],{"class":160,"line":319},[287,427,428],{},"@pytest.fixture\n",[287,430,431],{"class":160,"line":325},[287,432,433],{},"def cart():\n",[287,435,436],{"class":160,"line":331},[287,437,438],{},"    return Cart()\n",[287,440,441],{"class":160,"line":337},[287,442,305],{"emptyLinePlaceholder":304},[287,444,445],{"class":160,"line":343},[287,446,305],{"emptyLinePlaceholder":304},[287,448,449],{"class":160,"line":349},[287,450,451],{},"@given(st.lists(st.integers(min_value=1, max_value=100)))\n",[287,453,454],{"class":160,"line":355},[287,455,456],{},"def test_total_is_the_sum(cart, prices):      # health check fires\n",[287,458,459],{"class":160,"line":361},[287,460,461],{},"    for price in prices:\n",[287,463,464],{"class":160,"line":367},[287,465,466],{},"        cart.add(price)\n",[287,468,469],{"class":160,"line":373},[287,470,471],{},"    assert cart.total() == sum(prices)        # fails from example two onward\n",[278,473,475],{"className":280,"code":474,"language":282,"meta":283,"style":283},"from hypothesis import given, strategies as st\n\n\n# RIGHT: per-example state created inside the body.\n@given(st.lists(st.integers(min_value=1, max_value=100)))\ndef test_total_is_the_sum(prices):\n    cart = Cart()                             # fresh for every example\n    for price in prices:\n        cart.add(price)\n    assert cart.total() == sum(prices)\n",[25,476,477,481,485,489,494,498,503,508,512,516],{"__ignoreMap":283},[287,478,479],{"class":160,"line":289},[287,480,410],{},[287,482,483],{"class":160,"line":295},[287,484,305],{"emptyLinePlaceholder":304},[287,486,487],{"class":160,"line":301},[287,488,305],{"emptyLinePlaceholder":304},[287,490,491],{"class":160,"line":308},[287,492,493],{},"# RIGHT: per-example state created inside the body.\n",[287,495,496],{"class":160,"line":314},[287,497,451],{},[287,499,500],{"class":160,"line":319},[287,501,502],{},"def test_total_is_the_sum(prices):\n",[287,504,505],{"class":160,"line":325},[287,506,507],{},"    cart = Cart()                             # fresh for every example\n",[287,509,510],{"class":160,"line":331},[287,511,461],{},[287,513,514],{"class":160,"line":337},[287,515,466],{},[287,517,518],{"class":160,"line":343},[287,519,520],{},"    assert cart.total() == sum(prices)\n",[10,522,523,524,527,528,531],{},"The rule is simple: anything mutated by the test belongs inside the body. Fixtures may supply ",[73,525,526],{},"immutable"," configuration and ",[73,529,530],{},"expensive shared"," resources, never per-example state.",[273,533,535],{"id":534},"_3-give-database-backed-properties-their-own-transaction","3. Give database-backed properties their own transaction",[278,537,539],{"className":280,"code":538,"language":282,"meta":283,"style":283},"from hypothesis import given, settings, HealthCheck\n\n\n@settings(suppress_health_check=[HealthCheck.function_scoped_fixture],\n          max_examples=50, deadline=None)\n@given(invoices())\ndef test_persisted_invoice_round_trips(db_session, invoice):\n    # One savepoint per EXAMPLE, rolled back before the next one runs.\n    savepoint = db_session.begin_nested()\n    try:\n        db_session.add(invoice)\n        db_session.flush()\n        assert db_session.get(Invoice, invoice.id).total_minor == invoice.total_minor\n    finally:\n        savepoint.rollback()\n",[25,540,541,546,550,554,559,564,569,574,579,584,589,594,599,604,609],{"__ignoreMap":283},[287,542,543],{"class":160,"line":289},[287,544,545],{},"from hypothesis import given, settings, HealthCheck\n",[287,547,548],{"class":160,"line":295},[287,549,305],{"emptyLinePlaceholder":304},[287,551,552],{"class":160,"line":301},[287,553,305],{"emptyLinePlaceholder":304},[287,555,556],{"class":160,"line":308},[287,557,558],{},"@settings(suppress_health_check=[HealthCheck.function_scoped_fixture],\n",[287,560,561],{"class":160,"line":314},[287,562,563],{},"          max_examples=50, deadline=None)\n",[287,565,566],{"class":160,"line":319},[287,567,568],{},"@given(invoices())\n",[287,570,571],{"class":160,"line":325},[287,572,573],{},"def test_persisted_invoice_round_trips(db_session, invoice):\n",[287,575,576],{"class":160,"line":331},[287,577,578],{},"    # One savepoint per EXAMPLE, rolled back before the next one runs.\n",[287,580,581],{"class":160,"line":337},[287,582,583],{},"    savepoint = db_session.begin_nested()\n",[287,585,586],{"class":160,"line":343},[287,587,588],{},"    try:\n",[287,590,591],{"class":160,"line":349},[287,592,593],{},"        db_session.add(invoice)\n",[287,595,596],{"class":160,"line":355},[287,597,598],{},"        db_session.flush()\n",[287,600,601],{"class":160,"line":361},[287,602,603],{},"        assert db_session.get(Invoice, invoice.id).total_minor == invoice.total_minor\n",[287,605,606],{"class":160,"line":367},[287,607,608],{},"    finally:\n",[287,610,611],{"class":160,"line":373},[287,612,613],{},"        savepoint.rollback()\n",[10,615,616],{},"Suppressing the health check is legitimate here only because the body explicitly restores state per example. Suppressing it without that is how a property test starts passing for the wrong reason.",[273,618,620],{"id":619},"_4-use-the-django-integration-rather-than-fighting-it","4. Use the Django integration rather than fighting it",[278,622,624],{"className":280,"code":623,"language":282,"meta":283,"style":283},"from hypothesis import given\nfrom hypothesis.extra.django import TestCase, from_model\n\nfrom myapp.models import Customer\n\n\nclass CustomerProperties(TestCase):\n    \"\"\"Each example runs in its own nested atomic block, rolled back after.\"\"\"\n\n    @given(from_model(Customer, country=st.sampled_from([\"GB\", \"DE\"])))\n    def test_display_name_is_never_empty(self, customer):\n        assert customer.display_name().strip()\n",[25,625,626,631,636,640,645,649,653,658,663,667,672,677],{"__ignoreMap":283},[287,627,628],{"class":160,"line":289},[287,629,630],{},"from hypothesis import given\n",[287,632,633],{"class":160,"line":295},[287,634,635],{},"from hypothesis.extra.django import TestCase, from_model\n",[287,637,638],{"class":160,"line":301},[287,639,305],{"emptyLinePlaceholder":304},[287,641,642],{"class":160,"line":308},[287,643,644],{},"from myapp.models import Customer\n",[287,646,647],{"class":160,"line":314},[287,648,305],{"emptyLinePlaceholder":304},[287,650,651],{"class":160,"line":319},[287,652,305],{"emptyLinePlaceholder":304},[287,654,655],{"class":160,"line":325},[287,656,657],{},"class CustomerProperties(TestCase):\n",[287,659,660],{"class":160,"line":331},[287,661,662],{},"    \"\"\"Each example runs in its own nested atomic block, rolled back after.\"\"\"\n",[287,664,665],{"class":160,"line":337},[287,666,305],{"emptyLinePlaceholder":304},[287,668,669],{"class":160,"line":343},[287,670,671],{},"    @given(from_model(Customer, country=st.sampled_from([\"GB\", \"DE\"])))\n",[287,673,674],{"class":160,"line":349},[287,675,676],{},"    def test_display_name_is_never_empty(self, customer):\n",[287,678,679],{"class":160,"line":355},[287,680,681],{},"        assert customer.display_name().strip()\n",[10,683,684,687,688,691,692,695,696,28,699,702],{},[25,685,686],{},"hypothesis.extra.django.TestCase"," handles the per-example transaction automatically, and ",[25,689,690],{},"from_model"," derives a strategy from the model's field definitions — including ",[25,693,694],{},"max_length",", ",[25,697,698],{},"null",[25,700,701],{},"choices",", so generated instances are valid by construction.",[273,704,706],{"id":705},"_5-make-parallel-runs-safe","5. Make parallel runs safe",[278,708,710],{"className":280,"code":709,"language":282,"meta":283,"style":283},"# conftest.py\nimport os\n\nfrom hypothesis.database import DirectoryBasedExampleDatabase\nfrom hypothesis import settings\n\nworker = os.environ.get(\"PYTEST_XDIST_WORKER\", \"master\")\nsettings.register_profile(\n    \"xdist\",\n    database=DirectoryBasedExampleDatabase(f\".hypothesis\u002Fexamples-{worker}\"),\n)\n",[25,711,712,716,720,724,729,734,738,743,747,752,757],{"__ignoreMap":283},[287,713,714],{"class":160,"line":289},[287,715,292],{},[287,717,718],{"class":160,"line":295},[287,719,298],{},[287,721,722],{"class":160,"line":301},[287,723,305],{"emptyLinePlaceholder":304},[287,725,726],{"class":160,"line":308},[287,727,728],{},"from hypothesis.database import DirectoryBasedExampleDatabase\n",[287,730,731],{"class":160,"line":314},[287,732,733],{},"from hypothesis import settings\n",[287,735,736],{"class":160,"line":319},[287,737,305],{"emptyLinePlaceholder":304},[287,739,740],{"class":160,"line":325},[287,741,742],{},"worker = os.environ.get(\"PYTEST_XDIST_WORKER\", \"master\")\n",[287,744,745],{"class":160,"line":331},[287,746,328],{},[287,748,749],{"class":160,"line":337},[287,750,751],{},"    \"xdist\",\n",[287,753,754],{"class":160,"line":343},[287,755,756],{},"    database=DirectoryBasedExampleDatabase(f\".hypothesis\u002Fexamples-{worker}\"),\n",[287,758,759],{"class":160,"line":349},[287,760,364],{},[10,762,763],{},"Two workers writing to one example database produce intermittent, confusing failures on a directory that is not designed for concurrent writers. One directory per worker removes it for the cost of a few kilobytes.",[14,765,767],{"id":766},"verification","Verification",[10,769,770],{},"Check three things after wiring this up.",[278,772,776],{"className":773,"code":774,"language":775,"meta":283,"style":283},"language-bash shiki shiki-themes github-light github-dark","# 1. The right profile is active.\npytest tests\u002F -q --hypothesis-show-statistics 2>&1 | head -5\n","bash",[25,777,778,784],{"__ignoreMap":283},[287,779,780],{"class":160,"line":289},[287,781,783],{"class":782},"sJ8bj","# 1. The right profile is active.\n",[287,785,786,790,794,798,801,805,808,811],{"class":160,"line":295},[287,787,789],{"class":788},"sScJk","pytest",[287,791,793],{"class":792},"sZZnC"," tests\u002F",[287,795,797],{"class":796},"sj4cs"," -q",[287,799,800],{"class":796}," --hypothesis-show-statistics",[287,802,804],{"class":803},"szBVR"," 2>&1",[287,806,807],{"class":803}," |",[287,809,810],{"class":788}," head",[287,812,813],{"class":796}," -5\n",[278,815,819],{"className":816,"code":818,"language":130,"meta":283},[817],"language-text","Using Hypothesis profile 'ci' (max_examples=300, deadline=1000)\n",[25,820,818],{"__ignoreMap":283},[278,822,824],{"className":773,"code":823,"language":775,"meta":283,"style":283},"# 2. Health checks are not being suppressed silently across the suite.\ngrep -rn \"suppress_health_check\" tests\u002F | wc -l\n",[25,825,826,831],{"__ignoreMap":283},[287,827,828],{"class":160,"line":289},[287,829,830],{"class":782},"# 2. Health checks are not being suppressed silently across the suite.\n",[287,832,833,836,839,842,844,846,849],{"class":160,"line":295},[287,834,835],{"class":788},"grep",[287,837,838],{"class":796}," -rn",[287,840,841],{"class":792}," \"suppress_health_check\"",[287,843,793],{"class":792},[287,845,807],{"class":803},[287,847,848],{"class":788}," wc",[287,850,851],{"class":796}," -l\n",[278,853,855],{"className":773,"code":854,"language":775,"meta":283,"style":283},"# 3. A deliberate failure prints a reproducible blob.\npytest tests\u002Ftest_deliberate_failure.py -q 2>&1 | grep reproduce_failure\n",[25,856,857,862],{"__ignoreMap":283},[287,858,859],{"class":160,"line":289},[287,860,861],{"class":782},"# 3. A deliberate failure prints a reproducible blob.\n",[287,863,864,866,869,871,873,875,878],{"class":160,"line":295},[287,865,789],{"class":788},[287,867,868],{"class":792}," tests\u002Ftest_deliberate_failure.py",[287,870,797],{"class":796},[287,872,804],{"class":803},[287,874,807],{"class":803},[287,876,877],{"class":788}," grep",[287,879,880],{"class":792}," reproduce_failure\n",[278,882,885],{"className":883,"code":884,"language":130,"meta":283},[817],"@reproduce_failure('6.108.4', b'AXicY2BgYGRgYGBkYAQAAA4AAg==')\n",[25,886,884],{"__ignoreMap":283},[10,888,889],{},"A grep result of zero for the second check is suspicious in the other direction: a suite with database-backed properties and no suppressions probably has function-scoped fixtures producing warnings nobody reads.",[14,891,893],{"id":892},"troubleshooting","Troubleshooting",[895,896,897,913],"table",{},[898,899,900],"thead",{},[901,902,903,907,910],"tr",{},[904,905,906],"th",{},"Symptom",[904,908,909],{},"Root cause",[904,911,912],{},"Fix",[914,915,916,930,941,959,973,990],"tbody",{},[901,917,918,924,927],{},[919,920,921],"td",{},[25,922,923],{},"HealthCheck.function_scoped_fixture",[919,925,926],{},"Mutable fixture shared across examples",[919,928,929],{},"Create state inside the test body",[901,931,932,935,938],{},[919,933,934],{},"Property passes alone, fails in the suite",[919,936,937],{},"State leaking between examples or tests",[919,939,940],{},"Roll back per example; check autouse fixtures",[901,942,943,949,952],{},[919,944,945,948],{},[25,946,947],{},"DeadlineExceeded"," only in CI",[919,950,951],{},"Deadline tuned on a fast machine",[919,953,954,955,958],{},"Raise it, or set ",[25,956,957],{},"deadline=None"," for I\u002FO tests",[901,960,961,967,970],{},[919,962,963,964],{},"Flaky example-database errors under ",[25,965,966],{},"-n",[919,968,969],{},"Workers sharing one directory",[919,971,972],{},"One database directory per worker",[901,974,975,978,981],{},[919,976,977],{},"Cannot reproduce a CI failure",[919,979,980],{},"Seed and blob not recorded",[919,982,983,985,986,989],{},[25,984,386],{},"; upload ",[25,987,988],{},".hypothesis"," as an artefact",[901,991,992,995,1002],{},[919,993,994],{},"Tests slow after adding properties",[919,996,997,998,1001],{},"Default ",[25,999,1000],{},"max_examples"," in the dev loop",[919,1003,1004],{},"Profile-based budgets, small locally",[14,1006,1008],{"id":1007},"the-example-database-and-what-it-is-for","The example database, and what it is for",[10,1010,1011],{},"Hypothesis keeps a small on-disk database of the inputs that previously failed, and replays them first on the next run. That is what makes a fixed bug stay fixed locally: the failing example is tried again immediately rather than re-derived by chance.",[10,1013,1014],{},"In CI the value is different and often misunderstood. A fresh runner has an empty database, so nothing is replayed, and a run that finds a new failure writes it to a directory that is discarded when the job ends. Two responses are reasonable and they are not equivalent.",[10,1016,1017,1021,1022,1025,1026,1029],{},[1018,1019,1020],"strong",{},"Persist it as an artefact."," Uploading ",[25,1023,1024],{},".hypothesis\u002F"," on failure lets a developer download it and reproduce the exact example locally. Caching it ",[73,1027,1028],{},"between"," runs additionally makes CI re-try previous failures, at the cost of a cache that can go stale and hide a regression behind a replayed example.",[10,1031,1032,1035,1036,1039,1040,1043],{},[1018,1033,1034],{},"Disable it and rely on explicit examples."," ",[25,1037,1038],{},"database=None"," in the CI profile makes every run a clean search, and any failure worth keeping is promoted to an ",[25,1041,1042],{},"@example"," decorator in the source. This is the more disciplined option: a regression pinned in the code is visible in review and survives cache eviction, where one living only in a database directory does not.",[81,1045,1047,1125],{"className":1046},[84],[86,1048,94,1053,94,1056,94,1059,94,1063,94,1067,94,1070,94,1076,94,1081,94,1085,94,1088,94,1092,94,1096,94,1100,94,1102,94,1106,94,1110,94,1113,94,1116,94,1119,94,1122],{"viewBox":1049,"role":89,"ariaLabelledBy":1050,"xmlns":93},"0 0 820 264",[1051,1052],"db-t","db-d",[96,1054,1055],{"id":1051},"Two ways to handle the example database in CI",[100,1057,1058],{"id":1052},"Two approaches compared. Persisting the database as an artefact allows exact local reproduction and, if cached between runs, replays previous failures but risks a stale cache. Disabling the database keeps every run a clean search and requires promoting each regression to an explicit example decorator that is visible in review.",[122,1060],{"x":124,"y":124,"width":1061,"height":1062,"rx":127,"fill":128},"820","264",[130,1064,1066],{"x":1065,"y":133,"textAnchor":134,"fontSize":135,"fontWeight":136,"fill":120},"410","Reproduction versus a durable regression record",[122,1068],{"x":140,"y":230,"width":1069,"height":239,"rx":152,"fill":145,"stroke":146,"strokeWidth":147},"368",[130,1071,1075],{"x":1072,"y":1073,"textAnchor":134,"fontSize":1074,"fontWeight":136,"fill":120},"210","78","12.5","persist the database",[130,1077,1080],{"x":1078,"y":1079,"fontSize":144,"fill":178},"44","106","✓ exact local reproduction",[130,1082,1084],{"x":1078,"y":1083,"fontSize":144,"fill":178},"128","✓ replays prior failures if cached",[130,1086,1087],{"x":1078,"y":142,"fontSize":144,"fill":157},"✗ cache can hide a regression",[130,1089,1091],{"x":1078,"y":1090,"fontSize":144,"fill":157},"172","✗ nothing visible in review",[130,1093,1095],{"x":1078,"y":1094,"fontSize":144,"fontWeight":136,"fill":120},"204","upload .hypothesis\u002F on failure",[130,1097,1099],{"x":1078,"y":1098,"fontSize":144,"fill":120},"224","and keep print_blob on",[122,1101],{"x":256,"y":230,"width":1069,"height":239,"rx":152,"fill":171,"stroke":172,"strokeWidth":147},[130,1103,1105],{"x":1104,"y":1073,"textAnchor":134,"fontSize":1074,"fontWeight":136,"fill":120},"610","database=None in CI",[130,1107,1109],{"x":1108,"y":1079,"fontSize":144,"fill":178},"444","✓ every run is a clean search",[130,1111,1112],{"x":1108,"y":1083,"fontSize":144,"fill":178},"✓ regressions live in the source",[130,1114,1115],{"x":1108,"y":142,"fontSize":144,"fill":178},"✓ visible in code review",[130,1117,1118],{"x":1108,"y":1090,"fontSize":144,"fill":195},"✗ requires the discipline to add @example",[130,1120,1121],{"x":1108,"y":1094,"fontSize":144,"fontWeight":136,"fill":120},"promote every found bug to",[130,1123,1124],{"x":1108,"y":1098,"fontSize":144,"fill":120},"an @example in the test file",[265,1126,1127],{},"Both are defensible. What is not defensible is relying on an uncached CI database, which replays nothing and records nothing.",[14,1129,1131],{"id":1130},"reporting-and-statistics","Reporting and statistics",[10,1133,1134,1135,1138],{},"Property tests generate their own diagnostics, and reading them changes how the tests are written. ",[25,1136,1137],{},"--hypothesis-show-statistics"," prints, per test, how the budget was spent.",[278,1140,1142],{"className":773,"code":1141,"language":775,"meta":283,"style":283},"pytest tests\u002Ftest_parser.py --hypothesis-show-statistics -q\n",[25,1143,1144],{"__ignoreMap":283},[287,1145,1146,1148,1151,1153],{"class":160,"line":289},[287,1147,789],{"class":788},[287,1149,1150],{"class":792}," tests\u002Ftest_parser.py",[287,1152,800],{"class":796},[287,1154,1155],{"class":796}," -q\n",[278,1157,1160],{"className":1158,"code":1159,"language":130,"meta":283},[817],"  test_parser.py::test_round_trip:\n    - during generate phase (0.42 seconds):\n      - Typical runtimes: \u003C 1ms, ~ 54% in data generation\n      - 300 passing examples, 0 failing examples, 41 invalid examples\n    - Stopped because settings.max_examples=300\n",[25,1161,1159],{"__ignoreMap":283},[10,1163,1164,1165,1168,1169,1172,1173,1176,1177,1180],{},"Three numbers matter in that block. ",[1018,1166,1167],{},"Invalid examples"," are those rejected by ",[25,1170,1171],{},"assume()"," or a filter — forty-one out of three hundred is acceptable, three hundred out of six hundred is a strategy that needs restructuring. ",[1018,1174,1175],{},"Time in data generation"," above about half says the strategy is doing more work than the property; the usual cause is building heavyweight objects per example when the property only needs a few fields. And ",[1018,1178,1179],{},"the stopping reason"," tells you whether the budget was exhausted or the search gave up early, which distinguishes \"we searched properly\" from \"generation kept failing\".",[10,1182,1183,1184,1187,1188,1191],{},"The other reporting lever is ",[25,1185,1186],{},"event()",", which labels examples so the statistics show the distribution. Labelling by branch — ",[25,1189,1190],{},"event(\"cache_hit\" if hit else \"cache_miss\")"," — is the fastest way to discover that a property intended to exercise both paths has been taking one of them ninety-nine times in a hundred.",[14,1193,1195],{"id":1194},"marking-selecting-and-budgeting-in-ci","Marking, selecting and budgeting in CI",[10,1197,1198],{},"Property tests behave differently from example-based tests in a pipeline, and giving them their own marker makes that manageable.",[278,1200,1204],{"className":1201,"code":1202,"language":1203,"meta":283,"style":283},"language-toml shiki shiki-themes github-light github-dark","[tool.pytest.ini_options]\nmarkers = [\"property: generative test; budget scales with HYPOTHESIS_PROFILE\"]\n","toml",[25,1205,1206,1211],{"__ignoreMap":283},[287,1207,1208],{"class":160,"line":289},[287,1209,1210],{},"[tool.pytest.ini_options]\n",[287,1212,1213],{"class":160,"line":295},[287,1214,1215],{},"markers = [\"property: generative test; budget scales with HYPOTHESIS_PROFILE\"]\n",[278,1217,1219],{"className":773,"code":1218,"language":775,"meta":283,"style":283},"pytest -m \"not property\" -q                       # fast feedback, examples only\nHYPOTHESIS_PROFILE=ci pytest -m property -q       # the real search\nHYPOTHESIS_PROFILE=nightly pytest -m property -q  # deep search, scheduled\n",[25,1220,1221,1236,1261],{"__ignoreMap":283},[287,1222,1223,1225,1228,1231,1233],{"class":160,"line":289},[287,1224,789],{"class":788},[287,1226,1227],{"class":796}," -m",[287,1229,1230],{"class":792}," \"not property\"",[287,1232,797],{"class":796},[287,1234,1235],{"class":782},"                       # fast feedback, examples only\n",[287,1237,1238,1242,1245,1248,1251,1253,1256,1258],{"class":160,"line":295},[287,1239,1241],{"class":1240},"sVt8B","HYPOTHESIS_PROFILE",[287,1243,1244],{"class":803},"=",[287,1246,1247],{"class":792},"ci",[287,1249,1250],{"class":788}," pytest",[287,1252,1227],{"class":796},[287,1254,1255],{"class":792}," property",[287,1257,797],{"class":796},[287,1259,1260],{"class":782},"       # the real search\n",[287,1262,1263,1265,1267,1270,1272,1274,1276,1278],{"class":160,"line":301},[287,1264,1241],{"class":1240},[287,1266,1244],{"class":803},[287,1268,1269],{"class":792},"nightly",[287,1271,1250],{"class":788},[287,1273,1227],{"class":796},[287,1275,1255],{"class":792},[287,1277,797],{"class":796},[287,1279,1280],{"class":782},"  # deep search, scheduled\n",[10,1282,1283,1284,1287],{},"Separating them buys two things. A pull-request run stays fast and deterministic, which matters because a property test that finds a ",[73,1285,1286],{},"new"," failure on an unrelated change is confusing in review — the change did not break it, the search just got lucky. And the nightly deep run has somewhere to report to, so a genuine discovery becomes a ticket rather than a red build on somebody's unrelated pull request.",[10,1289,1290],{},"That distinction is worth making explicit to a team adopting properties. An example-based test failing means \"your change broke this\". A property test failing on a fresh seed means \"a latent bug was found\", which may predate the change entirely. Treating the second as a merge blocker makes people distrust the suite; routing it to a nightly job with an owner makes it a source of real defects.",[14,1292,1294],{"id":1293},"frameworks-beyond-django","Frameworks beyond Django",[10,1296,1297],{},"The integration pattern generalises. Hypothesis needs two things from a framework: a way to build valid domain objects, and a way to reset state between examples.",[10,1299,1300,1301,1304,1305,1309,1310,1313,1314,1317,1318,695,1321,1324],{},"For ",[1018,1302,1303],{},"SQLAlchemy",", build strategies from the mapped classes as shown in ",[51,1306,1308],{"href":1307},"\u002Fproperty-based-fuzz-testing-strategies\u002Fdesigning-strategies-for-domain-data\u002F","designing strategies for domain data",", and use a nested transaction per example. For ",[1018,1311,1312],{},"FastAPI"," or ",[1018,1315,1316],{},"Flask",", generate request payloads and assert on responses through the test client, with the application's dependency overrides supplying fakes so no network is involved. For ",[1018,1319,1320],{},"Pydantic",[25,1322,1323],{},"st.builds(Model)"," works directly once field types are registered, and validation errors become a legitimate outcome to assert on rather than an obstacle.",[278,1326,1328],{"className":280,"code":1327,"language":282,"meta":283,"style":283},"from hypothesis import given, strategies as st\nfrom pydantic import ValidationError\n\n\n@given(st.builds(dict, name=st.text(), age=st.integers()))\ndef test_model_either_validates_or_reports_precisely(payload):\n    try:\n        model = CustomerModel(**payload)\n    except ValidationError as exc:\n        # A rejection is fine; an unhelpful rejection is not.\n        assert exc.errors(), \"validation failed with no field information\"\n    else:\n        assert model.age >= 0\n",[25,1329,1330,1334,1339,1343,1347,1352,1357,1361,1366,1371,1376,1381,1386],{"__ignoreMap":283},[287,1331,1332],{"class":160,"line":289},[287,1333,410],{},[287,1335,1336],{"class":160,"line":295},[287,1337,1338],{},"from pydantic import ValidationError\n",[287,1340,1341],{"class":160,"line":301},[287,1342,305],{"emptyLinePlaceholder":304},[287,1344,1345],{"class":160,"line":308},[287,1346,305],{"emptyLinePlaceholder":304},[287,1348,1349],{"class":160,"line":314},[287,1350,1351],{},"@given(st.builds(dict, name=st.text(), age=st.integers()))\n",[287,1353,1354],{"class":160,"line":319},[287,1355,1356],{},"def test_model_either_validates_or_reports_precisely(payload):\n",[287,1358,1359],{"class":160,"line":325},[287,1360,588],{},[287,1362,1363],{"class":160,"line":331},[287,1364,1365],{},"        model = CustomerModel(**payload)\n",[287,1367,1368],{"class":160,"line":337},[287,1369,1370],{},"    except ValidationError as exc:\n",[287,1372,1373],{"class":160,"line":343},[287,1374,1375],{},"        # A rejection is fine; an unhelpful rejection is not.\n",[287,1377,1378],{"class":160,"line":349},[287,1379,1380],{},"        assert exc.errors(), \"validation failed with no field information\"\n",[287,1382,1383],{"class":160,"line":355},[287,1384,1385],{},"    else:\n",[287,1387,1388],{"class":160,"line":361},[287,1389,1390],{},"        assert model.age >= 0\n",[10,1392,1393],{},"That shape — \"either it succeeds and the invariant holds, or it fails with a usable error\" — is one of the most productive property templates for anything with a validation layer, because it covers the whole input space without requiring the test to know which inputs are valid.",[14,1395,1397],{"id":1396},"budgets-deadlines-and-the-feedback-loop","Budgets, deadlines and the feedback loop",[10,1399,1400],{},"Two numbers decide whether property tests are a pleasure or a tax, and both should differ by environment.",[10,1402,1403,1405],{},[25,1404,1000],{}," is the search budget. Twenty-five is plenty in an edit-run loop, where the point is to notice a break quickly; three hundred in CI does the real searching; a few thousand nightly explores the space properly. Running three hundred examples locally on every save is how teams conclude that property testing is too slow, when what is too slow is the profile.",[10,1407,1408,1411,1412,1414],{},[25,1409,1410],{},"deadline"," is a per-example time limit, and it exists to catch the pathological input that takes a hundred times longer than the typical one — a genuinely valuable signal for parsers and algorithms. It is also the setting most likely to produce a spurious CI failure, because a shared runner can pause any process for a second. The compromise that works: keep a deadline for pure computation, set ",[25,1413,957],{}," for anything touching I\u002FO, and never tune it so tightly that a busy machine trips it.",[10,1416,1417,1418,32],{},"One more interaction matters for large suites. Property tests multiply their cost by the number of examples, so a suite where every test is a property runs orders of magnitude longer than one where properties are used deliberately for the code that benefits — parsers, serializers, invariant-bearing data structures, state machines. Example-based tests remain the right tool for specific business rules with specific expected values, and the two are complementary rather than competing, as set out in ",[51,1419,1421],{"href":1420},"\u002Fproperty-based-fuzz-testing-strategies\u002F","property-based and fuzz testing strategies",[14,1423,1425],{"id":1424},"where-a-property-test-belongs-in-the-suite","Where a property test belongs in the suite",[10,1427,1428],{},"Adding properties to an existing suite works best when it is targeted rather than universal, and the targets are recognisable.",[10,1430,1431,1434,1435,32],{},[1018,1432,1433],{},"Anything with a round trip."," Serialize\u002Fdeserialize, encode\u002Fdecode, parse\u002Frender, save\u002Fload. The property writes itself and the bugs are real — see ",[51,1436,1438],{"href":1437},"\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002Fround-trip-properties-for-serializers-and-parsers\u002F","round-trip properties for serializers and parsers",[10,1440,1441,1444],{},[1018,1442,1443],{},"Anything with an algebraic law."," Sorting is idempotent, a merge is associative, a discount is never negative, a total is the sum of its parts. These are the properties a reviewer can check by reading.",[10,1446,1447,1450],{},[1018,1448,1449],{},"Anything with a reference implementation."," A fast path and a slow path that must agree, an optimised query and the naive one, a cache and the source of truth. Equivalence properties are the highest-value use of the tool and need no invention.",[10,1452,1453,1456,1457,1460,1461,1463,1464,32],{},[1018,1454,1455],{},"Anything stateful."," A cache, a connection pool, a state machine, a queue. These want ",[25,1458,1459],{},"RuleBasedStateMachine"," rather than ",[25,1462,48],{},", and they belong in ",[51,1465,1467],{"href":1466},"\u002Fproperty-based-fuzz-testing-strategies\u002Fstateful-and-model-based-testing\u002F","stateful and model-based testing",[81,1469,1471,1534],{"className":1470},[84],[86,1472,94,1477,94,1480,94,1483,94,1486,94,1489,94,1492,94,1495,94,1499,94,1503,94,1507,94,1511,94,1514,94,1516,94,1519,94,1522,94,1525,94,1528,94,1531],{"viewBox":1473,"role":89,"ariaLabelledBy":1474,"xmlns":93},"0 0 820 256",[1475,1476],"fit-t","fit-d",[96,1478,1479],{"id":1475},"Where generative tests pay off, and where examples remain better",[100,1481,1482],{"id":1476},"Two columns. Good fits for property tests are round trips, algebraic laws, equivalence against a reference implementation, and stateful components. Better left as example-based tests are specific business rules with named expected values, regression anchors for known bugs, and anything whose setup dominates its assertion.",[122,1484],{"x":124,"y":124,"width":1061,"height":1485,"rx":127,"fill":128},"256",[130,1487,1488],{"x":1065,"y":133,"textAnchor":134,"fontSize":135,"fontWeight":136,"fill":120},"Choosing the tool by the shape of the claim",[122,1490],{"x":140,"y":230,"width":1069,"height":1491,"rx":152,"fill":171,"stroke":172,"strokeWidth":147},"184",[130,1493,1494],{"x":1072,"y":1073,"textAnchor":134,"fontSize":1074,"fontWeight":136,"fill":120},"generative tests fit",[130,1496,1498],{"x":1078,"y":1497,"fontSize":240,"fill":120},"108","• round trips — encode then decode",[130,1500,1502],{"x":1078,"y":1501,"fontSize":240,"fill":120},"134","• laws — idempotence, associativity",[130,1504,1506],{"x":1078,"y":1505,"fontSize":240,"fill":120},"160","• equivalence to a reference impl",[130,1508,1510],{"x":1078,"y":1509,"fontSize":240,"fill":120},"186","• stateful components and caches",[130,1512,1513],{"x":1078,"y":169,"fontSize":144,"fill":178},"the claim is universal, so generate",[122,1515],{"x":256,"y":230,"width":1069,"height":1491,"rx":152,"fill":145,"stroke":146,"strokeWidth":147},[130,1517,1518],{"x":1104,"y":1073,"textAnchor":134,"fontSize":1074,"fontWeight":136,"fill":120},"examples stay better",[130,1520,1521],{"x":1108,"y":1497,"fontSize":240,"fill":120},"• a named rule with a named result",[130,1523,1524],{"x":1108,"y":1501,"fontSize":240,"fill":120},"• regression anchors for past bugs",[130,1526,1527],{"x":1108,"y":1505,"fontSize":240,"fill":120},"• setup-heavy integration paths",[130,1529,1530],{"x":1108,"y":1509,"fontSize":240,"fill":120},"• anything a reader must read once",[130,1532,1533],{"x":1108,"y":169,"fontSize":144,"fill":157},"the claim is specific, so state it",[265,1535,1536],{},"Most suites end up with a handful of properties covering the universal claims and a large number of examples covering the specific ones. Converting everything in either direction makes the suite worse.",[14,1538,1540],{"id":1539},"introducing-properties-to-a-team","Introducing properties to a team",[10,1542,1543],{},"The adoption failures are social rather than technical, and two habits avoid most of them.",[10,1545,1546],{},"Write the first properties against code that already has good example tests, so the properties find genuine latent bugs rather than merely restating what is already covered. A property that finds a real defect in week one makes the case for itself; one that only duplicates existing coverage looks like ceremony.",[10,1548,1549,1550,1552,1553,1555],{},"Then treat every discovered counterexample as two changes: the fix, and an ",[25,1551,1042],{}," decorator pinning the case. The pinned example is the artefact that persuades reviewers, because it turns a probabilistic search into a concrete regression test they can read. It also survives a future decision to disable the example database, which is the point at which teams otherwise lose their accumulated findings without noticing. A short convention written next to the profiles — every counterexample gets an ",[25,1554,1042],{},", every suppression gets a comment explaining what restores isolation — is enough governance for this, and it fits in a paragraph of the contributing guide.",[14,1557,1559],{"id":1558},"frequently-asked-questions","Frequently Asked Questions",[10,1561,1562,1565,1566,1568],{},[1018,1563,1564],{},"Why does Hypothesis warn about function-scoped fixtures?","\nBecause a function-scoped fixture is set up once per test function, not once per generated example, so every example after the first sees state left by the one before. Hypothesis raises ",[25,1567,923],{}," to say the test is not isolated. Either widen the fixture and reset state inside the test, or move the setup into the test body where it runs per example.",[10,1570,1571,1574,1575,1577],{},[1018,1572,1573],{},"How do I share a database between Hypothesis examples safely?","\nWrap each example in its own transaction inside the test body and roll it back, rather than relying on a per-test fixture. With Django, ",[25,1576,686],{}," does this for you by running each example in a nested atomic block.",[10,1579,1580,1583,1584,1587,1588,1590],{},[1018,1581,1582],{},"Do Hypothesis and pytest-xdist work together?","\nYes, with one caveat: the example database is a directory on disk, and parallel workers writing to it can conflict. Point each worker at its own database directory, or set the database to ",[25,1585,1586],{},"None"," in CI and rely on explicit ",[25,1589,1042],{}," entries for regressions.",[10,1592,1593,1596,1597,1599,1600,1603,1604,1606],{},[1018,1594,1595],{},"How do I reproduce a CI failure locally?","\nCopy the ",[25,1598,390],{}," decorator Hypothesis prints, or re-run with the printed seed via ",[25,1601,1602],{},"--hypothesis-seed",". Better still, persist the ",[25,1605,988],{}," directory as a CI artefact so the failing example is available without re-deriving it.",[10,1608,1609,1612,1613,1616,1617,1619],{},[1018,1610,1611],{},"Should max_examples differ between local runs and CI?","\nYes. A dev profile with a small budget keeps the edit-run loop fast; a CI profile with a larger one does the real searching. Register both with ",[25,1614,1615],{},"settings.register_profile"," and select with ",[25,1618,1241],{}," so nobody has to remember flags.",[14,1621,1623],{"id":1622},"related-guides","Related guides",[19,1625,1626,1633,1640,1647,1654],{},[22,1627,1628,1629,32],{},"Resolve the fixture question in ",[51,1630,1632],{"href":1631},"\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-integration-with-pytest-and-frameworks\u002Fcombining-given-with-pytest-fixtures-safely\u002F","combining @given with pytest fixtures safely",[22,1634,1635,1636,32],{},"Apply it to an ORM with ",[51,1637,1639],{"href":1638},"\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-integration-with-pytest-and-frameworks\u002Fproperty-testing-django-models-with-hypothesis\u002F","property-testing Django models with Hypothesis",[22,1641,1642,1643,32],{},"Make parallel runs safe using ",[51,1644,1646],{"href":1645},"\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-integration-with-pytest-and-frameworks\u002Frunning-hypothesis-under-pytest-xdist\u002F","running Hypothesis under pytest-xdist",[22,1648,1649,1650,32],{},"Keep the search from dominating the clock with ",[51,1651,1653],{"href":1652},"\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-framework-fundamentals\u002Freducing-hypothesis-test-execution-time\u002F","reducing Hypothesis test execution time",[22,1655,1656,1657,32],{},"Generate valid inputs in the first place via ",[51,1658,1308],{"href":1307},[10,1660,1661,1662],{},"← Back to ",[51,1663,1664],{"href":1420},"Property-Based & Fuzz Testing Strategies",[1666,1667,1668],"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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":283,"searchDepth":295,"depth":295,"links":1670},[1671,1672,1673,1680,1681,1682,1683,1684,1685,1686,1687,1688,1689,1690],{"id":16,"depth":295,"text":17},{"id":64,"depth":295,"text":65},{"id":270,"depth":295,"text":271,"children":1674},[1675,1676,1677,1678,1679],{"id":275,"depth":301,"text":276},{"id":394,"depth":301,"text":395},{"id":534,"depth":301,"text":535},{"id":619,"depth":301,"text":620},{"id":705,"depth":301,"text":706},{"id":766,"depth":295,"text":767},{"id":892,"depth":295,"text":893},{"id":1007,"depth":295,"text":1008},{"id":1130,"depth":295,"text":1131},{"id":1194,"depth":295,"text":1195},{"id":1293,"depth":295,"text":1294},{"id":1396,"depth":295,"text":1397},{"id":1424,"depth":295,"text":1425},{"id":1539,"depth":295,"text":1540},{"id":1558,"depth":295,"text":1559},{"id":1622,"depth":295,"text":1623},"Run Hypothesis alongside fixtures, Django, xdist and CI: function-scoped fixture pitfalls, profiles, database sharing, parallel seeds, and deterministic reproduction.","md",{"slug":1694,"type":1695,"breadcrumb":1696,"datePublished":1697,"dateModified":1697,"faq":1698,"howto":1709},"hypothesis-integration-with-pytest-and-frameworks","topic","Hypothesis Integration","2026-09-18",[1699,1701,1703,1705,1707],{"q":1564,"a":1700},"Because a function-scoped fixture is set up once per test function, not once per generated example, so every example after the first sees state left by the one before. Hypothesis raises HealthCheck.function_scoped_fixture to say the test is not isolated. Either widen the fixture and reset state inside the test, or move the setup into the test body where it runs per example.",{"q":1573,"a":1702},"Wrap each example in its own transaction inside the test body and roll it back, rather than relying on a per-test fixture. With Django, hypothesis.extra.django.TestCase does this for you by running each example in a nested atomic block.",{"q":1582,"a":1704},"Yes, with one caveat: the example database is a directory on disk, and parallel workers writing to it can conflict. Point each worker at its own database directory, or set the database to None in CI and rely on explicit @example entries for regressions.",{"q":1595,"a":1706},"Copy the @reproduce_failure decorator Hypothesis prints, or re-run with the printed seed via --hypothesis-seed. Better still, persist the .hypothesis directory as a CI artefact so the failing example is available without re-deriving it.",{"q":1611,"a":1708},"Yes. A dev profile with a small budget keeps the edit-run loop fast; a CI profile with a larger one does the real searching. Register both with settings.register_profile and select with HYPOTHESIS_PROFILE so nobody has to remember flags.",{"name":1710,"description":1711,"steps":1712},"How to run Hypothesis cleanly inside a pytest suite","Keep examples isolated, register per-environment profiles, and make failures reproducible across machines.",[1713,1716,1719,1722,1725],{"name":1714,"text":1715},"Register profiles per environment","Define dev, ci and nightly profiles with different max_examples and deadline values and select one from an environment variable.",{"name":1717,"text":1718},"Fix fixture scoping","Move per-example setup into the test body or widen the fixture, so state does not leak between generated examples.",{"name":1720,"text":1721},"Isolate the example database per worker","Give each xdist worker its own example database directory, or disable the database in CI and pin regressions with @example.",{"name":1723,"text":1724},"Persist the database as a CI artefact","Upload the .hypothesis directory so a failing example can be replayed locally without re-searching.",{"name":1726,"text":1727},"Pin every production bug as an explicit example","Add @example for each bug found in the wild so the case runs on every future execution regardless of seed.","\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-integration-with-pytest-and-frameworks",{"title":5,"description":1691},"property-based-fuzz-testing-strategies\u002Fhypothesis-integration-with-pytest-and-frameworks\u002Findex","dZqICfVHKURZExEBs0I991CcLA5PfSAoulNus4X1lRY",1789718765721]