[{"data":1,"prerenderedAt":1336},["ShallowReactive",2],{"page-\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002Fcomposing-strategies-with-flatmap-and-composite\u002F":3},{"id":4,"title":5,"body":6,"description":1296,"extension":1297,"meta":1298,"navigation":125,"path":1332,"seo":1333,"stem":1334,"__hash__":1335},"content\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002Fcomposing-strategies-with-flatmap-and-composite\u002Findex.md","Composing Strategies with flatmap and @composite",{"type":7,"value":8,"toc":1287},"minimark",[9,38,43,65,69,80,96,104,145,152,186,210,273,287,298,361,376,440,450,519,738,741,824,828,855,859,940,944,947,956,966,1023,1039,1073,1080,1183,1187,1208,1225,1243,1247,1276,1283],[10,11,12,13,18,19,23,24,23,27,23,30,33,34,37],"p",{},"You need ",[14,15,17],"a",{"href":16},"\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-framework-fundamentals\u002F","property-based testing with Hypothesis"," to generate data whose parts are not independent: a list and a valid index into it, a date range whose end is after its start, a discriminated union whose payload depends on its tag. Drawing the parts separately produces nonsense — an index past the end of the list, an end date before the start. The fix is strategy composition: ",[20,21,22],"code",{},"map",", ",[20,25,26],{},"filter",[20,28,29],{},"flatmap",[20,31,32],{},"builds",", and ",[20,35,36],{},"@st.composite"," each transform generation differently, and reaching for the wrong one is the difference between clean generation and a starved, health-check-tripping test. This guide draws the boundaries.",[39,40,42],"h2",{"id":41},"prerequisites","Prerequisites",[44,45,46,53],"ul",{},[47,48,49,52],"li",{},[20,50,51],{},"hypothesis >= 6.0",", Python 3.9+",[47,54,55,56,59,60,64],{},"The strategy basics from ",[14,57,58],{"href":16},"Hypothesis Framework Fundamentals"," and the custom-generator patterns in ",[14,61,63],{"href":62},"\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002Fgenerating-custom-strategies-with-hypothesisstrategies\u002F","Generating Custom Strategies with hypothesis.strategies",".",[39,66,68],{"id":67},"solution","Solution",[10,70,71,72,75,76,79],{},"All five combinators import from ",[20,73,74],{},"hypothesis.strategies"," (aliased ",[20,77,78],{},"st","):",[81,82,87],"pre",{"className":83,"code":84,"language":85,"meta":86,"style":86},"language-python shiki shiki-themes github-light github-dark","from hypothesis import given, strategies as st\n","python","",[20,88,89],{"__ignoreMap":86},[90,91,94],"span",{"class":92,"line":93},"line",1,[90,95,84],{},[10,97,98,103],{},[99,100,101],"strong",{},[20,102,22],{}," — apply a pure function to one drawn value. No dependency between values.",[81,105,107],{"className":83,"code":106,"language":85,"meta":86,"style":86},"# Generate even integers by doubling any integer.\neven_ints = st.integers().map(lambda n: n * 2)\n\n@given(even_ints)\ndef test_even(n):\n    assert n % 2 == 0\n",[20,108,109,114,120,127,133,139],{"__ignoreMap":86},[90,110,111],{"class":92,"line":93},[90,112,113],{},"# Generate even integers by doubling any integer.\n",[90,115,117],{"class":92,"line":116},2,[90,118,119],{},"even_ints = st.integers().map(lambda n: n * 2)\n",[90,121,123],{"class":92,"line":122},3,[90,124,126],{"emptyLinePlaceholder":125},true,"\n",[90,128,130],{"class":92,"line":129},4,[90,131,132],{},"@given(even_ints)\n",[90,134,136],{"class":92,"line":135},5,[90,137,138],{},"def test_even(n):\n",[90,140,142],{"class":92,"line":141},6,[90,143,144],{},"    assert n % 2 == 0\n",[10,146,147,151],{},[99,148,149],{},[20,150,26],{}," — reject drawn values that fail a predicate. Cheap only when the predicate passes most of the time; an over-strict filter starves the generator and trips a health check.",[81,153,155],{"className":83,"code":154,"language":85,"meta":86,"style":86},"# Fine: roughly half of integers pass.\nnonzero = st.integers().filter(lambda n: n != 0)\n\n@given(nonzero)\ndef test_nonzero(n):\n    assert n != 0\n",[20,156,157,162,167,171,176,181],{"__ignoreMap":86},[90,158,159],{"class":92,"line":93},[90,160,161],{},"# Fine: roughly half of integers pass.\n",[90,163,164],{"class":92,"line":116},[90,165,166],{},"nonzero = st.integers().filter(lambda n: n != 0)\n",[90,168,169],{"class":92,"line":122},[90,170,126],{"emptyLinePlaceholder":125},[90,172,173],{"class":92,"line":129},[90,174,175],{},"@given(nonzero)\n",[90,177,178],{"class":92,"line":135},[90,179,180],{},"def test_nonzero(n):\n",[90,182,183],{"class":92,"line":141},[90,184,185],{},"    assert n != 0\n",[10,187,188,192,193,197,198,201,202,205,206,209],{},[99,189,190],{},[20,191,29],{}," — the key tool for ",[194,195,196],"em",{},"interdependent"," values. ",[20,199,200],{},"flatmap(fn)"," draws a value, then calls ",[20,203,204],{},"fn(value)"," which must return a ",[194,207,208],{},"new strategy"," for the dependent part. Classic case: a list plus a valid index into it.",[81,211,213],{"className":83,"code":212,"language":85,"meta":86,"style":86},"# Draw a non-empty list, THEN draw an index that is valid for THAT list.\ndef list_and_index(xs):\n    # xs is already drawn; build a strategy for a valid index into it.\n    return st.tuples(st.just(xs), st.integers(min_value=0, max_value=len(xs) - 1))\n\nlist_with_valid_index = st.lists(st.integers(), min_size=1).flatmap(list_and_index)\n\n@given(list_with_valid_index)\ndef test_index_in_bounds(pair):\n    xs, i = pair\n    assert xs[i] == xs[i]   # never IndexError: i depends on len(xs)\n",[20,214,215,220,225,230,235,239,244,249,255,261,267],{"__ignoreMap":86},[90,216,217],{"class":92,"line":93},[90,218,219],{},"# Draw a non-empty list, THEN draw an index that is valid for THAT list.\n",[90,221,222],{"class":92,"line":116},[90,223,224],{},"def list_and_index(xs):\n",[90,226,227],{"class":92,"line":122},[90,228,229],{},"    # xs is already drawn; build a strategy for a valid index into it.\n",[90,231,232],{"class":92,"line":129},[90,233,234],{},"    return st.tuples(st.just(xs), st.integers(min_value=0, max_value=len(xs) - 1))\n",[90,236,237],{"class":92,"line":135},[90,238,126],{"emptyLinePlaceholder":125},[90,240,241],{"class":92,"line":141},[90,242,243],{},"list_with_valid_index = st.lists(st.integers(), min_size=1).flatmap(list_and_index)\n",[90,245,247],{"class":92,"line":246},7,[90,248,126],{"emptyLinePlaceholder":125},[90,250,252],{"class":92,"line":251},8,[90,253,254],{},"@given(list_with_valid_index)\n",[90,256,258],{"class":92,"line":257},9,[90,259,260],{},"def test_index_in_bounds(pair):\n",[90,262,264],{"class":92,"line":263},10,[90,265,266],{},"    xs, i = pair\n",[90,268,270],{"class":92,"line":269},11,[90,271,272],{},"    assert xs[i] == xs[i]   # never IndexError: i depends on len(xs)\n",[10,274,275,276,279,280,283,284,286],{},"If you tried this with two independent strategies (",[20,277,278],{},"st.tuples(st.lists(...), st.integers())","), most generated indices would be out of bounds and you would be forced into an ",[20,281,282],{},"assume()"," that discards most examples. ",[20,285,29],{}," makes the dependency structural.",[10,288,289,293,294,297],{},[99,290,291],{},[20,292,32],{}," — construct an object by drawing each constructor argument independently. Ideal for flat objects with ",[194,295,296],{},"no"," cross-field constraints.",[81,299,301],{"className":83,"code":300,"language":85,"meta":86,"style":86},"from dataclasses import dataclass\n\n@dataclass\nclass Point:\n    x: int\n    y: int\n\npoints = st.builds(Point, x=st.integers(), y=st.integers())\n\n@given(points)\ndef test_point(p):\n    assert isinstance(p, Point)\n",[20,302,303,308,312,317,322,327,332,336,341,345,350,355],{"__ignoreMap":86},[90,304,305],{"class":92,"line":93},[90,306,307],{},"from dataclasses import dataclass\n",[90,309,310],{"class":92,"line":116},[90,311,126],{"emptyLinePlaceholder":125},[90,313,314],{"class":92,"line":122},[90,315,316],{},"@dataclass\n",[90,318,319],{"class":92,"line":129},[90,320,321],{},"class Point:\n",[90,323,324],{"class":92,"line":135},[90,325,326],{},"    x: int\n",[90,328,329],{"class":92,"line":141},[90,330,331],{},"    y: int\n",[90,333,334],{"class":92,"line":246},[90,335,126],{"emptyLinePlaceholder":125},[90,337,338],{"class":92,"line":251},[90,339,340],{},"points = st.builds(Point, x=st.integers(), y=st.integers())\n",[90,342,343],{"class":92,"line":257},[90,344,126],{"emptyLinePlaceholder":125},[90,346,347],{"class":92,"line":263},[90,348,349],{},"@given(points)\n",[90,351,352],{"class":92,"line":269},[90,353,354],{},"def test_point(p):\n",[90,356,358],{"class":92,"line":357},12,[90,359,360],{},"    assert isinstance(p, Point)\n",[10,362,363,367,368,371,372,375],{},[99,364,365],{},[20,366,36],{}," — imperative, multi-draw generation when several fields depend on each other. The decorated function receives a ",[20,369,370],{},"draw"," callable as its first argument; each ",[20,373,374],{},"draw(strategy)"," pulls a concrete value, and you combine them however you like.",[81,377,379],{"className":83,"code":378,"language":85,"meta":86,"style":86},"@st.composite\ndef date_range(draw):\n    # draw() pulls concrete values; later draws can depend on earlier ones.\n    start = draw(st.integers(min_value=0, max_value=10_000))\n    span = draw(st.integers(min_value=0, max_value=365))\n    end = start + span            # guaranteed end >= start by construction\n    return (start, end)\n\n@given(date_range())\ndef test_range_ordered(r):\n    start, end = r\n    assert end >= start\n",[20,380,381,386,391,396,401,406,411,416,420,425,430,435],{"__ignoreMap":86},[90,382,383],{"class":92,"line":93},[90,384,385],{},"@st.composite\n",[90,387,388],{"class":92,"line":116},[90,389,390],{},"def date_range(draw):\n",[90,392,393],{"class":92,"line":122},[90,394,395],{},"    # draw() pulls concrete values; later draws can depend on earlier ones.\n",[90,397,398],{"class":92,"line":129},[90,399,400],{},"    start = draw(st.integers(min_value=0, max_value=10_000))\n",[90,402,403],{"class":92,"line":135},[90,404,405],{},"    span = draw(st.integers(min_value=0, max_value=365))\n",[90,407,408],{"class":92,"line":141},[90,409,410],{},"    end = start + span            # guaranteed end >= start by construction\n",[90,412,413],{"class":92,"line":246},[90,414,415],{},"    return (start, end)\n",[90,417,418],{"class":92,"line":251},[90,419,126],{"emptyLinePlaceholder":125},[90,421,422],{"class":92,"line":257},[90,423,424],{},"@given(date_range())\n",[90,426,427],{"class":92,"line":263},[90,428,429],{},"def test_range_ordered(r):\n",[90,431,432],{"class":92,"line":269},[90,433,434],{},"    start, end = r\n",[90,436,437],{"class":92,"line":357},[90,438,439],{},"    assert end >= start\n",[10,441,442,443,446,447,449],{},"A discriminated union — payload depends on the tag — is the canonical ",[20,444,445],{},"@composite"," shape, and shows why it beats ",[20,448,29],{}," once there are several interdependent draws:",[81,451,453],{"className":83,"code":452,"language":85,"meta":86,"style":86},"@st.composite\ndef event(draw):\n    kind = draw(st.sampled_from([\"login\", \"purchase\"]))\n    if kind == \"login\":\n        payload = {\"session\": draw(st.uuids())}\n    else:\n        payload = {\"amount\": draw(st.integers(min_value=1, max_value=10_000))}\n    return {\"kind\": kind, \"payload\": payload}\n\n@given(event())\ndef test_event_shape(e):\n    if e[\"kind\"] == \"purchase\":\n        assert e[\"payload\"][\"amount\"] >= 1\n",[20,454,455,459,464,469,474,479,484,489,494,498,503,508,513],{"__ignoreMap":86},[90,456,457],{"class":92,"line":93},[90,458,385],{},[90,460,461],{"class":92,"line":116},[90,462,463],{},"def event(draw):\n",[90,465,466],{"class":92,"line":122},[90,467,468],{},"    kind = draw(st.sampled_from([\"login\", \"purchase\"]))\n",[90,470,471],{"class":92,"line":129},[90,472,473],{},"    if kind == \"login\":\n",[90,475,476],{"class":92,"line":135},[90,477,478],{},"        payload = {\"session\": draw(st.uuids())}\n",[90,480,481],{"class":92,"line":141},[90,482,483],{},"    else:\n",[90,485,486],{"class":92,"line":246},[90,487,488],{},"        payload = {\"amount\": draw(st.integers(min_value=1, max_value=10_000))}\n",[90,490,491],{"class":92,"line":251},[90,492,493],{},"    return {\"kind\": kind, \"payload\": payload}\n",[90,495,496],{"class":92,"line":257},[90,497,126],{"emptyLinePlaceholder":125},[90,499,500],{"class":92,"line":263},[90,501,502],{},"@given(event())\n",[90,504,505],{"class":92,"line":269},[90,506,507],{},"def test_event_shape(e):\n",[90,509,510],{"class":92,"line":357},[90,511,512],{},"    if e[\"kind\"] == \"purchase\":\n",[90,514,516],{"class":92,"line":515},13,[90,517,518],{},"        assert e[\"payload\"][\"amount\"] >= 1\n",[520,521,524,734],"figure",{"className":522},[523],"diagram",[525,526,534,535,534,539,534,543,534,551,534,561,534,534,569,534,576,534,582,534,585,534,589,534,592,534,596,534,599,534,603,534,606,534,611,534,615,534,621,534,534,624,534,627,534,630,534,633,534,534,636,534,641,534,648,534,654,534,658,534,662,534,534,665,534,669,534,672,534,675,534,677,534,679,534,534,682,534,686,534,691,534,694,534,696,534,698,534,534,701,534,705,534,708,534,710,534,713,534,715,534,534,718,534,721,534,724,534,726,534,728,534,731],"svg",{"viewBox":527,"role":528,"ariaLabelledBy":529,"xmlns":532,"ariaLabel":533},"0 0 800 402","img",[530,531],"composeTitle","composeDesc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","Decision matrix mapping map, filter, flatmap, builds, and composite to the shape of dependency each one expresses","\n  ",[536,537,538],"title",{"id":530},"Choosing a strategy combinator by dependency shape",[540,541,542],"desc",{"id":531},"A matrix with one row per combinator and four capability columns. map and filter check the single-value column; flatmap checks the one-dependency column; builds checks the independent-fields column; composite checks the many-interdependent-draws column. A final column states what each is best for, so the checks form a diagonal from single-value transforms up to fully interdependent generation.",[544,545],"rect",{"x":546,"y":546,"width":547,"height":548,"rx":549,"fill":550},"0","800","402","14","#fffdf8",[552,553,560],"text",{"x":554,"y":555,"textAnchor":556,"fontSize":557,"fontWeight":558,"fill":559},"400","28","middle","18","700","currentColor","Which combinator: what dependency can it express?",[544,562],{"x":563,"y":564,"width":565,"height":566,"rx":549,"fill":567,"stroke":559,"strokeWidth":568},"20","48","760","326","none","1.5",[552,570,575],{"x":571,"y":572,"textAnchor":573,"fontSize":574,"fontWeight":558,"fill":559},"34","86","start","11.5","combinator",[552,577,581],{"x":578,"y":579,"textAnchor":556,"fontSize":580,"fill":559},"195","72","10.5","single",[552,583,584],{"x":578,"y":572,"textAnchor":556,"fontSize":580,"fill":559},"value",[552,586,588],{"x":587,"y":579,"textAnchor":556,"fontSize":580,"fill":559},"285","one",[552,590,591],{"x":587,"y":572,"textAnchor":556,"fontSize":580,"fill":559},"dependency",[552,593,595],{"x":594,"y":579,"textAnchor":556,"fontSize":580,"fill":559},"375","independent",[552,597,598],{"x":594,"y":572,"textAnchor":556,"fontSize":580,"fill":559},"fields",[552,600,602],{"x":601,"y":579,"textAnchor":556,"fontSize":580,"fill":559},"465","many inter-",[552,604,605],{"x":601,"y":572,"textAnchor":556,"fontSize":580,"fill":559},"dependent",[552,607,610],{"x":608,"y":572,"textAnchor":573,"fontSize":609,"fontWeight":558,"fill":559},"522","11","best for",[92,612],{"x1":563,"y1":613,"x2":614,"y2":613,"stroke":559,"strokeWidth":568},"98","780",[92,616],{"x1":617,"y1":564,"x2":617,"y2":618,"stroke":559,"strokeWidth":619,"opacity":620},"150","374","1","0.3",[92,622],{"x1":623,"y1":564,"x2":623,"y2":618,"stroke":559,"strokeWidth":619,"opacity":620},"510",[92,625],{"x1":563,"y1":617,"x2":614,"y2":617,"stroke":559,"strokeWidth":619,"opacity":626},"0.22",[92,628],{"x1":563,"y1":629,"x2":614,"y2":629,"stroke":559,"strokeWidth":619,"opacity":626},"202",[92,631],{"x1":563,"y1":632,"x2":614,"y2":632,"stroke":559,"strokeWidth":619,"opacity":626},"254",[92,634],{"x1":563,"y1":635,"x2":614,"y2":635,"stroke":559,"strokeWidth":619,"opacity":626},"306",[552,637,640],{"x":571,"y":638,"textAnchor":573,"fontSize":639,"fontWeight":558,"fill":559},"128","12.5",".map()",[642,643],"path",{"d":644,"fill":567,"stroke":645,"strokeWidth":646,"strokeLineCap":647,"strokeLineJoin":647},"M187 123 l5 6 l11 -14","#81b29a","2.6","round",[92,649],{"x1":650,"y1":651,"x2":652,"y2":651,"stroke":559,"strokeWidth":653,"opacity":620},"279","124","291","1.4",[92,655],{"x1":656,"y1":651,"x2":657,"y2":651,"stroke":559,"strokeWidth":653,"opacity":620},"369","381",[92,659],{"x1":660,"y1":651,"x2":661,"y2":651,"stroke":559,"strokeWidth":653,"opacity":620},"459","471",[552,663,664],{"x":608,"y":638,"textAnchor":573,"fontSize":609,"fill":559},"pure transform of a drawn value",[552,666,668],{"x":571,"y":667,"textAnchor":573,"fontSize":639,"fontWeight":558,"fill":559},"180",".filter()",[642,670],{"d":671,"fill":567,"stroke":645,"strokeWidth":646,"strokeLineCap":647,"strokeLineJoin":647},"M187 175 l5 6 l11 -14",[92,673],{"x1":650,"y1":674,"x2":652,"y2":674,"stroke":559,"strokeWidth":653,"opacity":620},"176",[92,676],{"x1":656,"y1":674,"x2":657,"y2":674,"stroke":559,"strokeWidth":653,"opacity":620},[92,678],{"x1":660,"y1":674,"x2":661,"y2":674,"stroke":559,"strokeWidth":653,"opacity":620},[552,680,681],{"x":608,"y":667,"textAnchor":573,"fontSize":609,"fill":559},"rare rejection (else it starves)",[552,683,685],{"x":571,"y":684,"textAnchor":573,"fontSize":639,"fontWeight":558,"fill":559},"232",".flatmap()",[92,687],{"x1":688,"y1":689,"x2":690,"y2":689,"stroke":559,"strokeWidth":653,"opacity":620},"189","228","201",[642,692],{"d":693,"fill":567,"stroke":645,"strokeWidth":646,"strokeLineCap":647,"strokeLineJoin":647},"M277 227 l5 6 l11 -14",[92,695],{"x1":656,"y1":689,"x2":657,"y2":689,"stroke":559,"strokeWidth":653,"opacity":620},[92,697],{"x1":660,"y1":689,"x2":661,"y2":689,"stroke":559,"strokeWidth":653,"opacity":620},[552,699,700],{"x":608,"y":684,"textAnchor":573,"fontSize":609,"fill":559},"a value plus one derived from it",[552,702,704],{"x":571,"y":703,"textAnchor":573,"fontSize":639,"fontWeight":558,"fill":559},"284","st.builds()",[92,706],{"x1":688,"y1":707,"x2":690,"y2":707,"stroke":559,"strokeWidth":653,"opacity":620},"280",[92,709],{"x1":650,"y1":707,"x2":652,"y2":707,"stroke":559,"strokeWidth":653,"opacity":620},[642,711],{"d":712,"fill":567,"stroke":645,"strokeWidth":646,"strokeLineCap":647,"strokeLineJoin":647},"M367 279 l5 6 l11 -14",[92,714],{"x1":660,"y1":707,"x2":661,"y2":707,"stroke":559,"strokeWidth":653,"opacity":620},[552,716,717],{"x":608,"y":703,"textAnchor":573,"fontSize":609,"fill":559},"flat object, no cross-field rules",[552,719,36],{"x":571,"y":720,"textAnchor":573,"fontSize":639,"fontWeight":558,"fill":559},"336",[92,722],{"x1":688,"y1":723,"x2":690,"y2":723,"stroke":559,"strokeWidth":653,"opacity":620},"332",[92,725],{"x1":650,"y1":723,"x2":652,"y2":723,"stroke":559,"strokeWidth":653,"opacity":620},[92,727],{"x1":656,"y1":723,"x2":657,"y2":723,"stroke":559,"strokeWidth":653,"opacity":620},[642,729],{"d":730,"fill":567,"stroke":645,"strokeWidth":646,"strokeLineCap":647,"strokeLineJoin":647},"M457 331 l5 6 l11 -14",[552,732,733],{"x":608,"y":720,"textAnchor":573,"fontSize":609,"fill":559},"fields that reference each other",[735,736,737],"figcaption",{},"Each combinator expresses exactly one shape of dependency; the checks fall on a diagonal from single-value transforms up to fully interdependent draws. Pick the row that matches how your values relate, not the most powerful tool.",[10,739,740],{},"The difference between the two is where the dependency between draws is expressed.",[520,742,744,821],{"className":743},[523],[525,745,534,750,534,753,534,756,534,759,534,765,534,773,534,775,534,779,534,784,534,788,534,791,534,795,534,798,534,800,534,803,534,807,534,810,534,813,534,816],{"viewBox":746,"role":528,"ariaLabelledBy":747,"xmlns":532},"0 0 760 212",[748,749],"compvsflat-t","compvsflat-d",[536,751,752],{"id":748},"flatmap and composite side by side",[540,754,755],{"id":749},"Two panels comparing flatmap and the composite decorator: how each expresses a dependent draw, how readable a three-step dependency is in each, and how each behaves during shrinking.",[544,757],{"x":546,"y":546,"width":565,"height":758,"rx":549,"fill":550},"212",[552,760,752],{"x":761,"y":762,"textAnchor":556,"fontSize":763,"fontWeight":558,"fill":764},"380","30","15","#3d405b",[544,766],{"x":767,"y":768,"width":769,"height":651,"rx":770,"fill":550,"stroke":771,"strokeWidth":772},"26","58","343","12","#f2cc8f","2",[544,774],{"x":767,"y":768,"width":769,"height":762,"rx":770,"fill":771},[552,776,29],{"x":777,"y":778,"textAnchor":556,"fontSize":770,"fontWeight":558,"fill":764},"198","78",[552,780,783],{"x":781,"y":782,"fontSize":609,"fill":764},"40","110","one dependency, inline",[552,785,787],{"x":781,"y":786,"fontSize":609,"fill":764},"130","value then strategy",[552,789,790],{"x":781,"y":617,"fontSize":609,"fill":764},"nests badly past two",[552,792,794],{"x":781,"y":793,"fontSize":609,"fill":764},"170","shrinks the outer first",[544,796],{"x":797,"y":768,"width":769,"height":651,"rx":770,"fill":550,"stroke":645,"strokeWidth":772},"391",[544,799],{"x":797,"y":768,"width":769,"height":762,"rx":770,"fill":764},[552,801,445],{"x":802,"y":778,"textAnchor":556,"fontSize":770,"fontWeight":558,"fill":550},"562",[552,804,806],{"x":805,"y":782,"fontSize":609,"fill":764},"405","many draws, imperative",[552,808,809],{"x":805,"y":786,"fontSize":609,"fill":764},"draw() reads like code",[552,811,812],{"x":805,"y":617,"fontSize":609,"fill":764},"stays flat at any depth",[552,814,815],{"x":805,"y":793,"fontSize":609,"fill":764},"shrinks each draw",[552,817,820],{"x":761,"y":818,"textAnchor":556,"fontSize":609,"fontStyle":819,"fill":764},"200","italic","Both build one strategy object; neither costs anything at test time.",[735,822,823],{},"Use flatmap for a single dependent step and @composite the moment a second draw depends on the first.",[39,825,827],{"id":826},"why-this-works","Why this works",[10,829,830,832,833,835,836,838,839,841,842,845,846,848,849,851,852,854],{},[20,831,22],{}," and ",[20,834,26],{}," are single-value transforms — one in, one out — so they cannot express that a second value depends on a first. ",[20,837,29],{}," closes that gap by handing you the drawn value and letting you return a tailored strategy, which is exactly enough for one dependency. ",[20,840,36],{}," generalizes this to arbitrarily many interdependent draws via repeated ",[20,843,844],{},"draw()"," calls, and because Hypothesis records every ",[20,847,370],{},", it can still shrink a composite example to its minimal failing form. ",[20,850,32],{}," is the optimized path for the independent case and should be preferred over ",[20,853,445],{}," when no field references another, because it lets Hypothesis use its internal flat generators.",[39,856,858],{"id":857},"edge-cases-and-failure-modes","Edge cases and failure modes",[44,860,861,880,896,916,930],{},[47,862,863,868,869,872,873,875,876,64],{},[99,864,865,867],{},[20,866,26],{}," starvation."," A predicate that rejects most candidates raises ",[20,870,871],{},"HealthCheck.filter_too_much","; move the constraint into generation (bounded strategy or ",[20,874,445],{},") instead. See ",[14,877,879],{"href":878},"\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-framework-fundamentals\u002Ffixing-hypothesis-flaky-health-check-failures\u002F","fixing Hypothesis FlakyHealthCheck failures",[47,881,882,889,890,892,893,895],{},[99,883,884,886,887,64],{},[20,885,29],{}," shrinks worse than ",[20,888,445],{}," Deeply nested ",[20,891,29],{}," chains can shrink poorly; for three or more interdependent values, prefer ",[20,894,445],{},", which Hypothesis shrinks more effectively.",[47,897,898,906,907,909,910,912,913,915],{},[99,899,900,901,903,904,64],{},"Calling ",[20,902,844],{}," outside ",[20,905,445],{}," ",[20,908,370],{}," is only valid inside a ",[20,911,36],{}," function; using it elsewhere raises an error. Conversely, never call a composite strategy's ",[20,914,370],{}," argument yourself — Hypothesis supplies it.",[47,917,918,923,924,926,927,929],{},[99,919,920,922],{},[20,921,32],{}," with interdependent fields."," Using ",[20,925,32],{}," when fields must agree produces invalid objects; switch to ",[20,928,445],{}," the moment one field's range depends on another's value.",[47,931,932,906,937,939],{},[99,933,934,936],{},[20,935,22],{}," with impure functions.",[20,938,22],{}," should be a pure transform; side effects run on every generation and every shrink attempt, which is wasteful and can corrupt shared state.",[39,941,943],{"id":942},"keeping-composite-strategies-shrinkable","Keeping composite strategies shrinkable",[10,945,946],{},"A composite strategy that generates good data but shrinks badly is worse than a simpler one, because the counterexample it reports is too large to read. Three habits keep shrinking effective.",[10,948,949,952,953,955],{},[99,950,951],{},"Draw in dependency order and nothing else."," Every ",[20,954,844],{}," call is a decision Hypothesis can simplify, and it simplifies them roughly in the order they were made. A strategy that draws a size, then draws that many elements, shrinks the size first and the elements second, which produces the minimal failing input naturally. A strategy that draws the elements first and then derives a size from them gives the shrinker nothing to pull on.",[10,957,958,906,961,832,963,965],{},[99,959,960],{},"Filter narrowly, and prefer construction over rejection.",[20,962,282],{},[20,964,668],{}," discard whole examples, and a filter that rejects most draws both slows generation and blocks shrinking, because the shrinker's smaller candidates keep getting rejected. Build the constraint into the draw instead:",[81,967,969],{"className":83,"code":968,"language":85,"meta":86,"style":86},"from hypothesis import strategies as st\n\n# Rejection: most draws are thrown away, and shrinking stalls.\nbad = st.tuples(st.integers(), st.integers()).filter(lambda t: t[0] \u003C t[1])\n\n# Construction: every draw is valid by design, and both values shrink freely.\n@st.composite\ndef ordered_pair(draw, min_value=0, max_value=1000):\n    low = draw(st.integers(min_value=min_value, max_value=max_value - 1))\n    high = draw(st.integers(min_value=low + 1, max_value=max_value))\n    return low, high            # invariant holds by construction, not by luck\n",[20,970,971,976,980,985,990,994,999,1003,1008,1013,1018],{"__ignoreMap":86},[90,972,973],{"class":92,"line":93},[90,974,975],{},"from hypothesis import strategies as st\n",[90,977,978],{"class":92,"line":116},[90,979,126],{"emptyLinePlaceholder":125},[90,981,982],{"class":92,"line":122},[90,983,984],{},"# Rejection: most draws are thrown away, and shrinking stalls.\n",[90,986,987],{"class":92,"line":129},[90,988,989],{},"bad = st.tuples(st.integers(), st.integers()).filter(lambda t: t[0] \u003C t[1])\n",[90,991,992],{"class":92,"line":135},[90,993,126],{"emptyLinePlaceholder":125},[90,995,996],{"class":92,"line":141},[90,997,998],{},"# Construction: every draw is valid by design, and both values shrink freely.\n",[90,1000,1001],{"class":92,"line":246},[90,1002,385],{},[90,1004,1005],{"class":92,"line":251},[90,1006,1007],{},"def ordered_pair(draw, min_value=0, max_value=1000):\n",[90,1009,1010],{"class":92,"line":257},[90,1011,1012],{},"    low = draw(st.integers(min_value=min_value, max_value=max_value - 1))\n",[90,1014,1015],{"class":92,"line":263},[90,1016,1017],{},"    high = draw(st.integers(min_value=low + 1, max_value=max_value))\n",[90,1019,1020],{"class":92,"line":269},[90,1021,1022],{},"    return low, high            # invariant holds by construction, not by luck\n",[10,1024,1025,1028,1029,1032,1033,1035,1036,1038],{},[99,1026,1027],{},"Return plain data, not objects that hide their provenance."," A composite that returns a fully built domain object is convenient, but when the test fails, Hypothesis prints the object's ",[20,1030,1031],{},"repr",". If that ",[20,1034,1031],{}," is uninformative, the counterexample tells you nothing. Either give the object a faithful ",[20,1037,1031],{},", or return the primitive draws alongside it so the report shows what was generated.",[81,1040,1042],{"className":83,"code":1041,"language":85,"meta":86,"style":86},"@st.composite\ndef order_with_lines(draw):\n    n = draw(st.integers(min_value=1, max_value=5))         # shrinks toward 1\n    prices = draw(st.lists(st.integers(1, 10_000), min_size=n, max_size=n))\n    # Returning both keeps the failing report readable.\n    return {\"line_count\": n, \"prices\": prices}\n",[20,1043,1044,1048,1053,1058,1063,1068],{"__ignoreMap":86},[90,1045,1046],{"class":92,"line":93},[90,1047,385],{},[90,1049,1050],{"class":92,"line":116},[90,1051,1052],{},"def order_with_lines(draw):\n",[90,1054,1055],{"class":92,"line":122},[90,1056,1057],{},"    n = draw(st.integers(min_value=1, max_value=5))         # shrinks toward 1\n",[90,1059,1060],{"class":92,"line":129},[90,1061,1062],{},"    prices = draw(st.lists(st.integers(1, 10_000), min_size=n, max_size=n))\n",[90,1064,1065],{"class":92,"line":135},[90,1066,1067],{},"    # Returning both keeps the failing report readable.\n",[90,1069,1070],{"class":92,"line":141},[90,1071,1072],{},"    return {\"line_count\": n, \"prices\": prices}\n",[10,1074,1075,1076,1079],{},"The payoff is visible the first time a suite fails on real data: a well-ordered composite reports ",[20,1077,1078],{},"{\"line_count\": 1, \"prices\": [1]}"," where a rejection-heavy one reports a twelve-element list that happens to contain the trigger.",[520,1081,1083,1180],{"className":1082},[523],[525,1084,534,1089,534,1092,534,1095,534,1112,534,1115,534,1117,534,1124,534,1129,534,1133,534,1138,534,1141,534,1145,534,1148,534,1152,534,1155,534,1159,534,1162,534,1166,534,1169,534,1173,534,1176],{"viewBox":1085,"role":528,"ariaLabelledBy":1086,"xmlns":532},"0 0 760 172",[1087,1088],"shrinkorder-t","shrinkorder-d",[536,1090,1091],{"id":1087},"How the shrinker walks a composite",[540,1093,1094],{"id":1088},"A left-to-right flow of shrinking a composite strategy: the failing example is recorded, each draw is simplified in the order it was made, every candidate re-runs the property, and the first minimal example that still fails is reported.",[1096,1097,1098,1099,534],"defs",{},"\n    ",[1100,1101,1108],"marker",{"id":1102,"viewBox":1103,"refX":1104,"refY":1105,"markerWidth":1106,"markerHeight":1106,"orient":1107},"shrinkorder-a","0 0 10 10","9","5","7","auto-start-reverse",[642,1109],{"d":1110,"fill":1111},"M0 0 L10 5 L0 10 z","#e07a5f",[544,1113],{"x":546,"y":546,"width":565,"height":1114,"rx":549,"fill":550},"172",[552,1116,1091],{"x":761,"y":762,"textAnchor":556,"fontSize":763,"fontWeight":558,"fill":764},[544,1118],{"x":767,"y":1119,"width":1120,"height":1121,"rx":770,"fill":1122,"stroke":1111,"strokeWidth":1123},"62","142","76","#f4f1de","1.8",[552,1125,1128],{"x":1126,"y":1127,"textAnchor":556,"fontSize":639,"fontWeight":558,"fill":764},"97","96","failure recorded",[552,1130,1132],{"x":1126,"y":1131,"textAnchor":556,"fontSize":609,"fill":764},"113","the full draw list",[92,1134],{"x1":674,"y1":1135,"x2":1136,"y2":1135,"stroke":1111,"strokeWidth":1123,"markerEnd":1137},"100","208","url(#shrinkorder-a)",[544,1139],{"x":1140,"y":1119,"width":1120,"height":1121,"rx":770,"fill":550,"stroke":1111,"strokeWidth":1123},"214",[552,1142,1144],{"x":1143,"y":1127,"textAnchor":556,"fontSize":639,"fontWeight":558,"fill":764},"286","simplify draw 1",[552,1146,1147],{"x":1143,"y":1131,"textAnchor":556,"fontSize":609,"fill":764},"earliest decision first",[92,1149],{"x1":1150,"y1":1135,"x2":1151,"y2":1135,"stroke":1111,"strokeWidth":1123,"markerEnd":1137},"364","396",[544,1153],{"x":1154,"y":1119,"width":1120,"height":1121,"rx":770,"fill":1122,"stroke":1111,"strokeWidth":1123},"403",[552,1156,1158],{"x":1157,"y":1127,"textAnchor":556,"fontSize":639,"fontWeight":558,"fill":764},"474","re-run property",[552,1160,1161],{"x":1157,"y":1131,"textAnchor":556,"fontSize":580,"fill":764},"candidate must still fail",[92,1163],{"x1":1164,"y1":1135,"x2":1165,"y2":1135,"stroke":1111,"strokeWidth":1123,"markerEnd":1137},"552","584",[544,1167],{"x":1168,"y":1119,"width":1120,"height":1121,"rx":770,"fill":550,"stroke":1111,"strokeWidth":1123},"592",[552,1170,1172],{"x":1171,"y":1127,"textAnchor":556,"fontSize":639,"fontWeight":558,"fill":764},"663","report minimum",[552,1174,1175],{"x":1171,"y":1131,"textAnchor":556,"fontSize":609,"fill":764},"smallest failing draw",[552,1177,1179],{"x":761,"y":1178,"textAnchor":556,"fontSize":574,"fontStyle":819,"fill":764},"164","A filter that rejects the simpler candidate stops the walk early.",[735,1181,1182],{},"Shrinking replays the property against progressively simpler draws, in the order the draws were made — which is why draw order is a design decision.",[39,1184,1186],{"id":1185},"frequently-asked-questions","Frequently Asked Questions",[10,1188,1189,1198,1199,1201,1202,1204,1205,1207],{},[99,1190,1191,1192,1194,1195,1197],{},"When should I use ",[20,1193,29],{}," instead of ",[20,1196,22],{}," in Hypothesis?","\nUse ",[20,1200,22],{}," when the transformation is a pure function of one drawn value. Use ",[20,1203,29],{}," when a later value depends on an earlier drawn value, because ",[20,1206,29],{}," receives the drawn value and must return a new strategy for the dependent part.",[10,1209,1210,1219,1221,1222,1224],{},[99,1211,1212,1213,1215,1216,1218],{},"What does the ",[20,1214,370],{}," function do inside an ",[20,1217,36],{}," strategy?",[20,1220,370],{}," pulls a concrete value from another strategy at generation time. Calling ",[20,1223,370],{}," multiple times lets you build interdependent values imperatively, and Hypothesis records every draw so it can shrink the composite example correctly.",[10,1226,1227,1237,1239,1240,1242],{},[99,1228,1229,1230,1233,1234,1236],{},"How is ",[20,1231,1232],{},"builds()"," different from ",[20,1235,445],{},"?",[20,1238,1232],{}," constructs an object by drawing each argument from a strategy independently and calling the target, ideal for flat objects with no cross-field constraints. ",[20,1241,445],{}," is for imperative, interdependent generation where one field's strategy depends on another field's drawn value.",[39,1244,1246],{"id":1245},"related","Related",[10,1248,1249,1250,1253,1254,1256,1257,1261,1262,1264,1265,1267,1268,1270,1271,1275],{},"These combinators are the building blocks for ",[14,1251,1252],{"href":62},"generating custom strategies with hypothesis.strategies",", where the same routing pattern keeps objects valid by construction. When a ",[20,1255,445],{}," chain slows a suite down, see ",[14,1258,1260],{"href":1259},"\u002Fproperty-based-fuzz-testing-strategies\u002Fhypothesis-framework-fundamentals\u002Freducing-hypothesis-test-execution-time\u002F","reducing Hypothesis test execution time",", and when an over-strict ",[20,1263,26],{}," trips a health check, ",[14,1266,879],{"href":878}," covers the diagnosis. The discriminated-union ",[20,1269,445],{}," shape here also underpins ",[14,1272,1274],{"href":1273},"\u002Fproperty-based-fuzz-testing-strategies\u002Fstateful-and-model-based-testing\u002Fmodeling-rest-apis-as-state-machines\u002F","modeling REST APIs as state machines",", where each transition draws a payload conditioned on the chosen command.",[10,1277,1278,1279],{},"← Back to ",[14,1280,1282],{"href":1281},"\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002F","Advanced Property-Based Testing",[1284,1285,1286],"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);}",{"title":86,"searchDepth":116,"depth":116,"links":1288},[1289,1290,1291,1292,1293,1294,1295],{"id":41,"depth":116,"text":42},{"id":67,"depth":116,"text":68},{"id":826,"depth":116,"text":827},{"id":857,"depth":116,"text":858},{"id":942,"depth":116,"text":943},{"id":1185,"depth":116,"text":1186},{"id":1245,"depth":116,"text":1246},"Build interdependent Hypothesis data with map, filter, flatmap, builds, and @st.composite using draw() — when each transforms generation and which to reach for.","md",{"slug":1299,"type":1300,"breadcrumb":1301,"datePublished":1302,"dateModified":1302,"faq":1303,"howto":1313},"composing-strategies-with-flatmap-and-composite","article","flatmap & @composite","2026-06-18",[1304,1307,1310],{"q":1305,"a":1306},"When should I use flatmap instead of map in Hypothesis?","Use map when the transformation is a pure function of one drawn value. Use flatmap when a later value depends on an earlier drawn value, because flatmap receives the drawn value and must return a new strategy for the dependent part.",{"q":1308,"a":1309},"What does the draw function do inside an @st.composite strategy?","draw pulls a concrete value from another strategy at generation time. Calling draw multiple times lets you build interdependent values imperatively, and Hypothesis records every draw so it can shrink the composite example correctly.",{"q":1311,"a":1312},"How is builds() different from @composite?","builds() constructs an object by drawing each argument from a strategy independently and calling the target, which is ideal for flat objects with no cross-field constraints. @composite is for imperative, interdependent generation where one field's strategy depends on another field's drawn value.",{"name":1314,"description":1315,"steps":1316},"How to compose interdependent Hypothesis strategies","Choose between map, filter, flatmap, builds, and @composite to generate values whose parts depend on each other.",[1317,1320,1323,1326,1329],{"name":1318,"text":1319},"Transform with map","Apply a pure function to a single drawn value using strategy.map(fn) when no later value depends on it.",{"name":1321,"text":1322},"Constrain with filter","Reject unwanted values with strategy.filter(pred), but only for predicates that pass most of the time.",{"name":1324,"text":1325},"Chain dependencies with flatmap","Use strategy.flatmap(fn) when a value drawn first determines the strategy for a value drawn next.",{"name":1327,"text":1328},"Assemble flat objects with builds","Construct objects whose fields are independent with st.builds(Target, field=strategy).",{"name":1330,"text":1331},"Use @composite for interdependence","Write an @st.composite function and call draw() multiple times to build values that reference each other.","\u002Fproperty-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002Fcomposing-strategies-with-flatmap-and-composite",{"title":5,"description":1296},"property-based-fuzz-testing-strategies\u002Fadvanced-property-based-testing\u002Fcomposing-strategies-with-flatmap-and-composite\u002Findex","_5wSmrVJUah46ntYBU5pLoLcFKv8I9CFrcWrjEnN4Yk",1785613404289]