[{"data":1,"prerenderedAt":1103},["ShallowReactive",2],{"page-\u002Fintegration-database-and-service-testing\u002Ftest-data-factories-and-builders\u002Fgenerating-reproducible-fake-data-with-faker\u002F":3},{"id":4,"title":5,"body":6,"description":1066,"extension":1067,"meta":1068,"navigation":105,"path":1099,"seo":1100,"stem":1101,"__hash__":1102},"content\u002Fintegration-database-and-service-testing\u002Ftest-data-factories-and-builders\u002Fgenerating-reproducible-fake-data-with-faker\u002Findex.md","Generating Reproducible Fake Data with Faker",{"type":7,"value":8,"toc":1055},"minimark",[9,22,27,58,62,65,263,313,427,431,445,452,456,512,516,519,525,531,537,592,697,700,703,707,710,776,783,791,795,804,859,871,977,991,995,1001,1007,1013,1017,1046,1051],[10,11,12,13,17,18,21],"p",{},"Faker makes test data look realistic, and realistic data finds bugs that ",[14,15,16],"code",{},"\"test\""," and ",[14,19,20],{},"\"foo\""," never do: the apostrophe in O'Brien, the non-ASCII character in Zoë, the email that is valid and ninety characters long. It also makes tests fail at random, because the generator reaches those values only occasionally. Seeding it correctly keeps the first property and removes the second.",[23,24,26],"h2",{"id":25},"prerequisites","Prerequisites",[28,29,30,45,50],"ul",{},[31,32,33,36,37,40,41,44],"li",{},[14,34,35],{},"Faker >= 25",", or the ",[14,38,39],{},"factory.Faker"," wrapper that ships with ",[14,42,43],{},"factory_boy",".",[31,46,47,44],{},[14,48,49],{},"pytest >= 8.0",[31,51,52,53,44],{},"A clear line between values the test asserts on and values it merely needs to exist — see ",[54,55,57],"a",{"href":56},"\u002Fintegration-database-and-service-testing\u002Ftest-data-factories-and-builders\u002F","test data factories and builders",[23,59,61],{"id":60},"solution","Solution",[10,63,64],{},"Print a session seed, derive a stable per-test seed from it, and hand each test a seeded instance.",[66,67,72],"pre",{"className":68,"code":69,"language":70,"meta":71,"style":71},"language-python shiki shiki-themes github-light github-dark","# conftest.py\nimport hashlib\nimport os\nimport random\n\nimport pytest\nfrom faker import Faker\n\n\ndef pytest_addoption(parser):\n    parser.addoption(\"--faker-seed\", type=int, default=None,\n                     help=\"reproduce a run's generated data\")\n\n\n@pytest.fixture(scope=\"session\")\ndef faker_session_seed(request):\n    seed = request.config.getoption(\"--faker-seed\")\n    if seed is None:\n        seed = int(os.environ.get(\"FAKER_SEED\", random.randrange(2**31)))\n    # Printed in the header so every failure report carries it.\n    print(f\"\\nFaker session seed: {seed}  (re-run with --faker-seed={seed})\")\n    return seed\n\n\n@pytest.fixture\ndef fake(request, faker_session_seed):\n    instance = Faker(\"en_GB\")                     # pinned locale, not the machine's\n    # Per-test seed: stable no matter which tests ran before this one.\n    digest = hashlib.sha256(f\"{faker_session_seed}:{request.node.nodeid}\".encode())\n    instance.seed_instance(int.from_bytes(digest.digest()[:8], \"big\"))\n    yield instance\n    instance.unique.clear()                        # unique pools are per test\n","python","",[14,73,74,82,88,94,100,107,113,119,124,129,135,141,147,152,157,163,169,175,181,187,193,199,205,210,215,221,227,233,239,245,251,257],{"__ignoreMap":71},[75,76,79],"span",{"class":77,"line":78},"line",1,[75,80,81],{},"# conftest.py\n",[75,83,85],{"class":77,"line":84},2,[75,86,87],{},"import hashlib\n",[75,89,91],{"class":77,"line":90},3,[75,92,93],{},"import os\n",[75,95,97],{"class":77,"line":96},4,[75,98,99],{},"import random\n",[75,101,103],{"class":77,"line":102},5,[75,104,106],{"emptyLinePlaceholder":105},true,"\n",[75,108,110],{"class":77,"line":109},6,[75,111,112],{},"import pytest\n",[75,114,116],{"class":77,"line":115},7,[75,117,118],{},"from faker import Faker\n",[75,120,122],{"class":77,"line":121},8,[75,123,106],{"emptyLinePlaceholder":105},[75,125,127],{"class":77,"line":126},9,[75,128,106],{"emptyLinePlaceholder":105},[75,130,132],{"class":77,"line":131},10,[75,133,134],{},"def pytest_addoption(parser):\n",[75,136,138],{"class":77,"line":137},11,[75,139,140],{},"    parser.addoption(\"--faker-seed\", type=int, default=None,\n",[75,142,144],{"class":77,"line":143},12,[75,145,146],{},"                     help=\"reproduce a run's generated data\")\n",[75,148,150],{"class":77,"line":149},13,[75,151,106],{"emptyLinePlaceholder":105},[75,153,155],{"class":77,"line":154},14,[75,156,106],{"emptyLinePlaceholder":105},[75,158,160],{"class":77,"line":159},15,[75,161,162],{},"@pytest.fixture(scope=\"session\")\n",[75,164,166],{"class":77,"line":165},16,[75,167,168],{},"def faker_session_seed(request):\n",[75,170,172],{"class":77,"line":171},17,[75,173,174],{},"    seed = request.config.getoption(\"--faker-seed\")\n",[75,176,178],{"class":77,"line":177},18,[75,179,180],{},"    if seed is None:\n",[75,182,184],{"class":77,"line":183},19,[75,185,186],{},"        seed = int(os.environ.get(\"FAKER_SEED\", random.randrange(2**31)))\n",[75,188,190],{"class":77,"line":189},20,[75,191,192],{},"    # Printed in the header so every failure report carries it.\n",[75,194,196],{"class":77,"line":195},21,[75,197,198],{},"    print(f\"\\nFaker session seed: {seed}  (re-run with --faker-seed={seed})\")\n",[75,200,202],{"class":77,"line":201},22,[75,203,204],{},"    return seed\n",[75,206,208],{"class":77,"line":207},23,[75,209,106],{"emptyLinePlaceholder":105},[75,211,213],{"class":77,"line":212},24,[75,214,106],{"emptyLinePlaceholder":105},[75,216,218],{"class":77,"line":217},25,[75,219,220],{},"@pytest.fixture\n",[75,222,224],{"class":77,"line":223},26,[75,225,226],{},"def fake(request, faker_session_seed):\n",[75,228,230],{"class":77,"line":229},27,[75,231,232],{},"    instance = Faker(\"en_GB\")                     # pinned locale, not the machine's\n",[75,234,236],{"class":77,"line":235},28,[75,237,238],{},"    # Per-test seed: stable no matter which tests ran before this one.\n",[75,240,242],{"class":77,"line":241},29,[75,243,244],{},"    digest = hashlib.sha256(f\"{faker_session_seed}:{request.node.nodeid}\".encode())\n",[75,246,248],{"class":77,"line":247},30,[75,249,250],{},"    instance.seed_instance(int.from_bytes(digest.digest()[:8], \"big\"))\n",[75,252,254],{"class":77,"line":253},31,[75,255,256],{},"    yield instance\n",[75,258,260],{"class":77,"line":259},32,[75,261,262],{},"    instance.unique.clear()                        # unique pools are per test\n",[66,264,266],{"className":68,"code":265,"language":70,"meta":71,"style":71},"def test_customer_round_trips_through_the_api(api, fake):\n    payload = {\n        \"name\": fake.name(),                     # realistic, never asserted on\n        \"email\": fake.unique.email(),            # unique within this test\n        \"vat_exempt\": True,                      # asserted on: explicit\n    }\n    created = api.post(\"\u002Fcustomers\", json=payload).json()\n    assert created[\"vat_exempt\"] is True\n    assert created[\"name\"] == payload[\"name\"]    # round trip, not a literal\n",[14,267,268,273,278,283,288,293,298,303,308],{"__ignoreMap":71},[75,269,270],{"class":77,"line":78},[75,271,272],{},"def test_customer_round_trips_through_the_api(api, fake):\n",[75,274,275],{"class":77,"line":84},[75,276,277],{},"    payload = {\n",[75,279,280],{"class":77,"line":90},[75,281,282],{},"        \"name\": fake.name(),                     # realistic, never asserted on\n",[75,284,285],{"class":77,"line":96},[75,286,287],{},"        \"email\": fake.unique.email(),            # unique within this test\n",[75,289,290],{"class":77,"line":102},[75,291,292],{},"        \"vat_exempt\": True,                      # asserted on: explicit\n",[75,294,295],{"class":77,"line":109},[75,296,297],{},"    }\n",[75,299,300],{"class":77,"line":115},[75,301,302],{},"    created = api.post(\"\u002Fcustomers\", json=payload).json()\n",[75,304,305],{"class":77,"line":121},[75,306,307],{},"    assert created[\"vat_exempt\"] is True\n",[75,309,310],{"class":77,"line":126},[75,311,312],{},"    assert created[\"name\"] == payload[\"name\"]    # round trip, not a literal\n",[314,315,318,419],"figure",{"className":316},[317],"diagram",[319,320,327,328,327,332,327,336,327,344,327,354,327,364,327,370,327,376,327,380,327,384,327,389,327,393,327,398,327,402,327,406,327,409,327,412,327,416],"svg",{"viewBox":321,"role":322,"ariaLabelledBy":323,"xmlns":326},"0 0 820 262","img",[324,325],"fk-t","fk-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[329,330,331],"title",{"id":324},"Session seed versus per-test seed",[333,334,335],"desc",{"id":325},"Two arrangements. With one shared seed, each test's data depends on how many values earlier tests consumed, so inserting a test changes the data of every test after it. With a per-test seed derived from the session seed and the node id, each test's data is fixed regardless of order or of tests added elsewhere.",[337,338],"rect",{"x":339,"y":339,"width":340,"height":341,"rx":342,"fill":343},"0","820","262","14","#fffdf8",[345,346,353],"text",{"x":347,"y":348,"textAnchor":349,"fontSize":350,"fontWeight":351,"fill":352},"410","28","middle","16","700","#3d405b","Where each test's random stream starts",[337,355],{"x":356,"y":357,"width":358,"height":359,"rx":360,"fill":361,"stroke":362,"strokeWidth":363},"26","52","368","186","12","#fbe9e3","#e07a5f","2",[345,365,369],{"x":366,"y":367,"textAnchor":349,"fontSize":368,"fontWeight":351,"fill":352},"210","78","12.5","one shared seed",[345,371,375],{"x":372,"y":373,"fontSize":374,"fill":352},"44","106","11","test A consumes values 1–5",[345,377,379],{"x":372,"y":378,"fontSize":374,"fill":352},"128","test B gets values 6–9",[345,381,383],{"x":372,"y":382,"fontSize":374,"fill":352},"150","insert a test before B →",[345,385,388],{"x":372,"y":386,"fontSize":374,"fill":387},"172","#8f3d22","B now gets different data",[345,390,392],{"x":372,"y":391,"fontSize":374,"fontWeight":351,"fill":387},"206","failures move when tests are added",[337,394],{"x":395,"y":357,"width":358,"height":359,"rx":360,"fill":396,"stroke":397,"strokeWidth":363},"426","#e6f0ea","#81b29a",[345,399,401],{"x":400,"y":367,"textAnchor":349,"fontSize":368,"fontWeight":351,"fill":352},"610","seed per test",[345,403,405],{"x":404,"y":373,"fontSize":374,"fill":352},"444","seed = hash(session, nodeid)",[345,407,408],{"x":404,"y":378,"fontSize":374,"fill":352},"each test starts its own stream",[345,410,411],{"x":404,"y":382,"fontSize":374,"fill":352},"order and neighbours irrelevant",[345,413,415],{"x":404,"y":386,"fontSize":374,"fill":414},"#2a5f49","-k one_test reproduces exactly",[345,417,418],{"x":404,"y":391,"fontSize":374,"fontWeight":351,"fill":414},"a failure stays where it is",[420,421,422,423,426],"figcaption",{},"The per-test derivation is what makes ",[14,424,425],{},"pytest -k failing_test --faker-seed=N"," reproduce the failing data. With a shared seed, running one test alone gives it different values.",[23,428,430],{"id":429},"why-this-works","Why this works",[10,432,433,436,437,440,441,444],{},[14,434,435],{},"Faker.seed_instance"," resets an instance's private random generator, so every value it produces afterwards follows a deterministic sequence. Hashing the session seed together with the node id gives each test its own starting point that depends only on those two inputs — not on how many values earlier tests consumed, not on whether the test was selected with ",[14,438,439],{},"-k",", not on ",[14,442,443],{},"pytest-randomly"," shuffling the order.",[10,446,447,448,451],{},"The printed seed is the half that makes this useful in CI. A failure caused by generated data carries the seed in its header, and re-running with ",[14,449,450],{},"--faker-seed"," regenerates exactly the same values for exactly that test.",[23,453,455],{"id":454},"edge-cases-and-failure-modes","Edge cases and failure modes",[28,457,458,471,488,497,503],{},[31,459,460,470],{},[461,462,463,466,467,44],"strong",{},[14,464,465],{},"Faker.seed()"," versus ",[14,468,469],{},"seed_instance()"," The class method seeds a shared generator used by every instance, so two fixtures seeding it race each other. Always seed the instance.",[31,472,473,479,480,483,484,487],{},[461,474,475,478],{},[14,476,477],{},"unique"," exhaustion."," ",[14,481,482],{},"fake.unique.first_name()"," raises ",[14,485,486],{},"UniquenessException"," once the pool is used up. Clear it per test, and prefer sequences for anything that needs thousands of unique values.",[31,489,490,479,493,496],{},[461,491,492],{},"Locale drift.",[14,494,495],{},"Faker()"," with no argument uses a default locale, and output formats differ between locales. Pin it.",[31,498,499,502],{},[461,500,501],{},"Faker version upgrades."," Provider word lists change between releases, so the same seed produces different values after an upgrade. That is fine as long as nothing asserts on a generated literal.",[31,504,505,479,508,511],{},[461,506,507],{},"Generated values in assertions.",[14,509,510],{},"assert customer.name == \"Gemma Hughes\""," passes until the next Faker release. Assert on round trips or on values the test supplied.",[23,513,515],{"id":514},"deciding-which-values-may-be-generated","Deciding which values may be generated",[10,517,518],{},"The rule \"never assert on generated values\" is easy to state and easy to break by accident, so it helps to classify every field a test touches before deciding where its value comes from.",[10,520,521,524],{},[461,522,523],{},"Asserted fields"," are the ones the test's outcome depends on: the amount that must be refunded, the status that must change, the flag that must be respected. These are always passed explicitly, in the test, where the reader can see them.",[10,526,527,530],{},[461,528,529],{},"Incidental fields"," are required for the object to be valid but play no part in the behaviour: a customer's name on an order-total test, an address on a VAT test. These are the natural home for generated data, and generating them is actively useful because it exercises the code with varied realistic input on every run.",[10,532,533,536],{},[461,534,535],{},"Constrained fields"," must satisfy a rule the generator does not know about: a unique email, a postcode matching a country, a date after another date. These need either a unique proxy, a derivation from another field, or an explicit value — never a bare generator call.",[66,538,540],{"className":68,"code":539,"language":70,"meta":71,"style":71},"def test_refund_covers_the_full_amount(api, fake):\n    order = an_order(\n        amount_minor=4_999,                  # asserted: explicit\n        customer_name=fake.name(),           # incidental: generated\n        customer_email=fake.unique.email(),  # constrained: unique proxy\n        country=\"GB\",\n        postcode=fake.postcode(),            # constrained: locale-pinned to GB\n    )\n    refund = api.refund(order.id)\n    assert refund.amount_minor == 4_999\n",[14,541,542,547,552,557,562,567,572,577,582,587],{"__ignoreMap":71},[75,543,544],{"class":77,"line":78},[75,545,546],{},"def test_refund_covers_the_full_amount(api, fake):\n",[75,548,549],{"class":77,"line":84},[75,550,551],{},"    order = an_order(\n",[75,553,554],{"class":77,"line":90},[75,555,556],{},"        amount_minor=4_999,                  # asserted: explicit\n",[75,558,559],{"class":77,"line":96},[75,560,561],{},"        customer_name=fake.name(),           # incidental: generated\n",[75,563,564],{"class":77,"line":102},[75,565,566],{},"        customer_email=fake.unique.email(),  # constrained: unique proxy\n",[75,568,569],{"class":77,"line":109},[75,570,571],{},"        country=\"GB\",\n",[75,573,574],{"class":77,"line":115},[75,575,576],{},"        postcode=fake.postcode(),            # constrained: locale-pinned to GB\n",[75,578,579],{"class":77,"line":121},[75,580,581],{},"    )\n",[75,583,584],{"class":77,"line":126},[75,585,586],{},"    refund = api.refund(order.id)\n",[75,588,589],{"class":77,"line":131},[75,590,591],{},"    assert refund.amount_minor == 4_999\n",[314,593,595,690],{"className":594},[317],[319,596,327,601,327,604,327,607,327,611,327,616,327,622,327,625,327,630,327,635,327,639,327,642,327,645,327,648,327,650,327,653,327,657,327,660,327,663,327,666,327,670,327,672,327,676,327,680,327,683,327,687],{"viewBox":597,"role":322,"ariaLabelledBy":598,"xmlns":326},"0 0 800 234",[599,600],"cls-t","cls-d",[329,602,603],{"id":599},"Three kinds of field and where each value comes from",[333,605,606],{"id":600},"Asserted fields, which the outcome depends on, are passed explicitly. Incidental fields, which must be valid but play no part in the behaviour, are generated. Constrained fields, which must satisfy a rule the generator does not know, use a unique proxy, a derivation or an explicit value.",[337,608],{"x":339,"y":339,"width":609,"height":610,"rx":342,"fill":343},"800","234",[345,612,615],{"x":613,"y":348,"textAnchor":349,"fontSize":614,"fontWeight":351,"fill":352},"400","15.5","Classify first, then choose the source",[337,617],{"x":618,"y":619,"width":620,"height":621,"rx":360,"fill":343,"stroke":362,"strokeWidth":363},"24","50","240","164",[337,623],{"x":618,"y":619,"width":620,"height":624,"rx":360,"fill":352},"30",[345,626,629],{"x":627,"y":628,"textAnchor":349,"fontSize":360,"fontWeight":351,"fill":343},"144","70","asserted",[345,631,634],{"x":632,"y":633,"fontSize":374,"fill":352},"40","104","outcome depends on it",[345,636,638],{"x":632,"y":637,"fontSize":374,"fill":352},"126","amount, status, flags",[345,640,641],{"x":632,"y":621,"fontSize":374,"fontWeight":351,"fill":387},"always explicit",[345,643,644],{"x":632,"y":359,"fontSize":374,"fill":352},"visible in the test",[337,646],{"x":647,"y":619,"width":620,"height":621,"rx":360,"fill":343,"stroke":397,"strokeWidth":363},"280",[337,649],{"x":647,"y":619,"width":620,"height":624,"rx":360,"fill":352},[345,651,652],{"x":613,"y":628,"textAnchor":349,"fontSize":360,"fontWeight":351,"fill":343},"incidental",[345,654,656],{"x":655,"y":633,"fontSize":374,"fill":352},"296","must be valid, plays",[345,658,659],{"x":655,"y":637,"fontSize":374,"fill":352},"no part in behaviour",[345,661,662],{"x":655,"y":621,"fontSize":374,"fontWeight":351,"fill":414},"generate freely",[345,664,665],{"x":655,"y":359,"fontSize":374,"fill":352},"variety finds bugs",[337,667],{"x":668,"y":619,"width":620,"height":621,"rx":360,"fill":343,"stroke":669,"strokeWidth":363},"536","#f2cc8f",[337,671],{"x":668,"y":619,"width":620,"height":624,"rx":360,"fill":352},[345,673,675],{"x":674,"y":628,"textAnchor":349,"fontSize":360,"fontWeight":351,"fill":343},"656","constrained",[345,677,679],{"x":678,"y":633,"fontSize":374,"fill":352},"552","unique, derived or",[345,681,682],{"x":678,"y":637,"fontSize":374,"fill":352},"locale-dependent",[345,684,686],{"x":678,"y":621,"fontSize":374,"fontWeight":351,"fill":685},"#8a5a00","unique proxy or derive",[345,688,689],{"x":678,"y":359,"fontSize":374,"fill":352},"never a bare call",[420,691,692,693,696],{},"Most flaky Faker usage is a constrained field treated as incidental — a bare ",[14,694,695],{},"fake.email()"," on a column with a unique index.",[10,698,699],{},"Writing the classification into the test, as the comments above do, costs nothing and makes review straightforward: a reviewer can see at a glance whether any asserted field is being generated, which is the one mistake that turns realistic data into a flaky test.",[10,701,702],{},"The same classification also guides upgrades. When a new Faker release changes a provider's output, only tests that were quietly depending on generated values fail — and every one of those is a test that had an asserted or constrained field mis-classified as incidental. Treating an upgrade's failures as a list of classification bugs to fix, rather than as a reason to pin the old version, leaves the suite better than it was and keeps the dependency current. Pinning Faker to avoid those failures, by contrast, simply defers them to a larger and more confusing upgrade later, when many more tests have accumulated the same hidden dependency on specific generated strings.",[23,704,706],{"id":705},"making-generated-data-find-bugs-on-purpose","Making generated data find bugs on purpose",[10,708,709],{},"Realistic data is most valuable when it is steered toward the values that break code. Faker's providers accept arguments and can be combined with a small amount of deliberate edge-case mixing.",[66,711,713],{"className":68,"code":712,"language":70,"meta":71,"style":71},"import random\n\n\ndef awkward_name(fake: \"Faker\", rng: random.Random) -> str:\n    # Mostly realistic, sometimes deliberately hostile.\n    return rng.choice([\n        fake.name(),\n        fake.name(),\n        \"O'Brien\",                 # apostrophe: SQL and CSV escaping\n        \"Zoë Ångström\",            # non-ASCII: encoding and collation\n        \"李小龙\",                    # CJK: width and sorting\n        \"A\" * 255,                 # length limit\n    ])\n",[14,714,715,719,723,727,732,737,742,747,751,756,761,766,771],{"__ignoreMap":71},[75,716,717],{"class":77,"line":78},[75,718,99],{},[75,720,721],{"class":77,"line":84},[75,722,106],{"emptyLinePlaceholder":105},[75,724,725],{"class":77,"line":90},[75,726,106],{"emptyLinePlaceholder":105},[75,728,729],{"class":77,"line":96},[75,730,731],{},"def awkward_name(fake: \"Faker\", rng: random.Random) -> str:\n",[75,733,734],{"class":77,"line":102},[75,735,736],{},"    # Mostly realistic, sometimes deliberately hostile.\n",[75,738,739],{"class":77,"line":109},[75,740,741],{},"    return rng.choice([\n",[75,743,744],{"class":77,"line":115},[75,745,746],{},"        fake.name(),\n",[75,748,749],{"class":77,"line":121},[75,750,746],{},[75,752,753],{"class":77,"line":126},[75,754,755],{},"        \"O'Brien\",                 # apostrophe: SQL and CSV escaping\n",[75,757,758],{"class":77,"line":131},[75,759,760],{},"        \"Zoë Ångström\",            # non-ASCII: encoding and collation\n",[75,762,763],{"class":77,"line":137},[75,764,765],{},"        \"李小龙\",                    # CJK: width and sorting\n",[75,767,768],{"class":77,"line":143},[75,769,770],{},"        \"A\" * 255,                 # length limit\n",[75,772,773],{"class":77,"line":149},[75,774,775],{},"    ])\n",[10,777,778,779,782],{},"Seeding ",[14,780,781],{},"rng"," from the same per-test seed keeps this reproducible. The mix finds encoding and escaping bugs within the first few runs, and because the seed is printed, a failure on the CJK name reproduces exactly.",[10,784,785,786,790],{},"This is as far as Faker should be pushed. When the goal becomes systematically exploring which inputs break a function — rather than filling fields with plausible values — the right tool is ",[54,787,789],{"href":788},"\u002Fproperty-based-fuzz-testing-strategies\u002F","property-based testing with Hypothesis",", which searches the input space deliberately and shrinks a failure to its minimal cause.",[23,792,794],{"id":793},"seeding-factory_boys-faker-too","Seeding factory_boy's Faker too",[10,796,797,798,800,801,803],{},"Suites that use ",[14,799,39],{}," in factory declarations have a second generator to control, because ",[14,802,43],{}," keeps its own Faker instances rather than using the one a fixture provides.",[66,805,807],{"className":68,"code":806,"language":70,"meta":71,"style":71},"# conftest.py\nimport factory.random\nimport pytest\n\n\n@pytest.fixture(autouse=True)\ndef seed_factories(request, faker_session_seed):\n    digest = hashlib.sha256(f\"{faker_session_seed}:{request.node.nodeid}\".encode())\n    # Seeds factory_boy's shared random state, which its Faker attributes and\n    # fuzzy attributes both draw from.\n    factory.random.reseed_random(int.from_bytes(digest.digest()[:8], \"big\"))\n",[14,808,809,813,818,822,826,830,835,840,844,849,854],{"__ignoreMap":71},[75,810,811],{"class":77,"line":78},[75,812,81],{},[75,814,815],{"class":77,"line":84},[75,816,817],{},"import factory.random\n",[75,819,820],{"class":77,"line":90},[75,821,112],{},[75,823,824],{"class":77,"line":96},[75,825,106],{"emptyLinePlaceholder":105},[75,827,828],{"class":77,"line":102},[75,829,106],{"emptyLinePlaceholder":105},[75,831,832],{"class":77,"line":109},[75,833,834],{},"@pytest.fixture(autouse=True)\n",[75,836,837],{"class":77,"line":115},[75,838,839],{},"def seed_factories(request, faker_session_seed):\n",[75,841,842],{"class":77,"line":121},[75,843,244],{},[75,845,846],{"class":77,"line":126},[75,847,848],{},"    # Seeds factory_boy's shared random state, which its Faker attributes and\n",[75,850,851],{"class":77,"line":131},[75,852,853],{},"    # fuzzy attributes both draw from.\n",[75,855,856],{"class":77,"line":137},[75,857,858],{},"    factory.random.reseed_random(int.from_bytes(digest.digest()[:8], \"big\"))\n",[10,860,861,862,865,866,870],{},"Reusing the same derivation as the ",[14,863,864],{},"fake"," fixture means one printed session seed reproduces ",[867,868,869],"em",{},"both"," generators for any test, which matters because a failure caused by a factory-generated name looks identical to one caused by a fixture-generated name in the report.",[314,872,874,974],{"className":873},[317],[319,875,327,879,327,882,327,885,327,902,327,904,327,907,327,914,327,919,327,923,327,930,327,935,327,939,327,942,327,946,327,949,327,956,327,961,327,964,327,967,327,970],{"viewBox":597,"role":322,"ariaLabelledBy":876,"xmlns":326},[877,878],"two-gen-t","two-gen-d",[329,880,881],{"id":877},"One session seed controlling two generators",[333,883,884],{"id":878},"A single printed session seed is combined with the test's node id to derive a per-test seed. That seed is applied both to the fake fixture's Faker instance and to factory_boy's shared random state, so re-running with the same session seed reproduces every generated value in the test regardless of which mechanism produced it.",[886,887,888,889,327],"defs",{},"\n    ",[890,891,898],"marker",{"id":892,"viewBox":893,"refX":894,"refY":895,"markerWidth":896,"markerHeight":896,"orient":897},"two-gen-a","0 0 10 10","9","5","7","auto-start-reverse",[899,900],"path",{"d":901,"fill":397},"M0 0 L10 5 L0 10 z",[337,903],{"x":339,"y":339,"width":609,"height":610,"rx":342,"fill":343},[345,905,906],{"x":613,"y":348,"textAnchor":349,"fontSize":614,"fontWeight":351,"fill":352},"One seed in the report reproduces everything",[337,908],{"x":909,"y":910,"width":911,"height":912,"rx":374,"fill":913,"stroke":669,"strokeWidth":363},"34","86","190","66","#f7f0da",[345,915,918],{"x":916,"y":917,"textAnchor":349,"fontSize":360,"fontWeight":351,"fill":352},"129","112","session seed",[345,920,922],{"x":916,"y":921,"textAnchor":349,"fontSize":374,"fill":685},"134","printed in the header",[77,924],{"x1":925,"y1":926,"x2":927,"y2":926,"stroke":397,"strokeWidth":928,"markerEnd":929},"228","119","268","1.8","url(#two-gen-a)",[337,931],{"x":932,"y":910,"width":366,"height":912,"rx":374,"fill":933,"stroke":352,"strokeWidth":934},"274","#f4f1de","1.6",[345,936,938],{"x":937,"y":917,"textAnchor":349,"fontSize":360,"fontWeight":351,"fill":352},"379","hash(seed, nodeid)",[345,940,941],{"x":937,"y":921,"textAnchor":349,"fontSize":374,"fill":352},"per-test seed",[77,943],{"x1":944,"y1":373,"x2":668,"y2":945,"stroke":397,"strokeWidth":928,"markerEnd":929},"488","74",[77,947],{"x1":944,"y1":948,"x2":668,"y2":621,"stroke":397,"strokeWidth":928,"markerEnd":929},"132",[337,950],{"x":951,"y":952,"width":953,"height":954,"rx":955,"fill":396,"stroke":397,"strokeWidth":363},"542","46","230","56","10",[345,957,960],{"x":958,"y":628,"textAnchor":349,"fontSize":959,"fontWeight":351,"fill":352},"657","11.5","fake fixture",[345,962,469],{"x":958,"y":963,"textAnchor":349,"fontSize":374,"fill":352},"89",[337,965],{"x":951,"y":966,"width":953,"height":954,"rx":955,"fill":396,"stroke":397,"strokeWidth":363},"138",[345,968,43],{"x":958,"y":969,"textAnchor":349,"fontSize":959,"fontWeight":351,"fill":352},"162",[345,971,973],{"x":958,"y":972,"textAnchor":349,"fontSize":374,"fill":352},"181","reseed_random()",[420,975,976],{},"Seeding only one of the two leaves half the generated data random, which is enough to make a failure irreproducible.",[10,978,979,980,983,984,986,987,990],{},"Under ",[14,981,982],{},"pytest-xdist"," the arrangement needs no change: each worker computes the same per-test seed for the same node id, so a failure on worker three reproduces locally with the printed seed and ",[14,985,439],{}," alone. That independence from execution topology is the property a shared, sequential seed can never have, and it is the reason to prefer the derivation over the simpler global ",[14,988,989],{},"Faker.seed(n)"," that most tutorials show.",[23,992,994],{"id":993},"frequently-asked-questions","Frequently Asked Questions",[10,996,997,1000],{},[461,998,999],{},"Why does a test using Faker fail only occasionally?","\nBecause the generator produced a value the code cannot handle — an apostrophe in a name, a duplicate email, a date on a boundary — and it does so only when the random sequence happens to reach it. Seed the generator, print the seed, and the failing run becomes reproducible on demand.",[10,1002,1003,1006],{},[461,1004,1005],{},"Should Faker be seeded once per session or once per test?","\nPer test, derived from a session seed and the test's node id. A single session seed makes each test's data depend on how many values earlier tests consumed, so adding or reordering tests changes every later test's input. A per-test seed keeps each test's data stable regardless of the rest of the suite.",[10,1008,1009,1012],{},[461,1010,1011],{},"Is Faker a substitute for property-based testing?","\nNo. Faker produces realistic-looking values for filling fields; it does not search for failing inputs or shrink a failure to a minimal case. When the goal is to find the inputs that break the code, Hypothesis is the tool, and Faker remains useful for readable fixtures and demo data.",[23,1014,1016],{"id":1015},"related","Related",[28,1018,1019,1025,1032,1039],{},[31,1020,1021,1024],{},[54,1022,1023],{"href":56},"Test Data Factories & Builders"," — where Faker-backed factory attributes fit.",[31,1026,1027,1031],{},[54,1028,1030],{"href":1029},"\u002Fintegration-database-and-service-testing\u002Ftest-data-factories-and-builders\u002Ffactory-boy-versus-plain-fixture-builders\u002F","factory_boy versus Plain Fixture Builders"," — choosing the mechanism that calls Faker.",[31,1033,1034,1038],{},[54,1035,1037],{"href":1036},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fcontrolling-time-and-randomness-in-tests\u002Fseeding-random-and-numpy-for-reproducible-tests\u002F","Seeding random and NumPy for Reproducible Tests"," — the same discipline for other generators.",[31,1040,1041,1045],{},[54,1042,1044],{"href":1043},"\u002Fproperty-based-fuzz-testing-strategies\u002Fdesigning-strategies-for-domain-data\u002F","Designing Strategies for Domain Data"," — when generation should search rather than fill.",[10,1047,1048,1049],{},"← Back to ",[54,1050,1023],{"href":56},[1052,1053,1054],"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":71,"searchDepth":84,"depth":84,"links":1056},[1057,1058,1059,1060,1061,1062,1063,1064,1065],{"id":25,"depth":84,"text":26},{"id":60,"depth":84,"text":61},{"id":429,"depth":84,"text":430},{"id":454,"depth":84,"text":455},{"id":514,"depth":84,"text":515},{"id":705,"depth":84,"text":706},{"id":793,"depth":84,"text":794},{"id":993,"depth":84,"text":994},{"id":1015,"depth":84,"text":1016},"Use Faker in tests without flakiness: session and per-test seeding, printing the seed, unique proxies, locale choice, and keeping asserted values out of generated data.","md",{"slug":1069,"type":1070,"breadcrumb":1071,"datePublished":1072,"dateModified":1072,"faq":1073,"howto":1080},"generating-reproducible-fake-data-with-faker","article","Reproducible Faker","2026-09-18",[1074,1076,1078],{"q":999,"a":1075},"Because the generator produced a value the code cannot handle — an apostrophe in a name, a duplicate email, a date on a boundary — and it does so only when the random sequence happens to reach it. Seed the generator, print the seed, and the failing run becomes reproducible on demand.",{"q":1005,"a":1077},"Per test, derived from a session seed and the test's node id. A single session seed makes each test's data depend on how many values earlier tests consumed, so adding or reordering tests changes every later test's input. A per-test seed keeps each test's data stable regardless of the rest of the suite.",{"q":1011,"a":1079},"No. Faker produces realistic-looking values for filling fields; it does not search for failing inputs or shrink a failure to a minimal case. When the goal is to find the inputs that break the code, Hypothesis is the tool, and Faker remains useful for readable fixtures and demo data.",{"name":1081,"description":1082,"steps":1083},"How to use Faker reproducibly in tests","Seed per test from a printed session seed, use the unique proxy for constrained fields, and never assert on generated values.",[1084,1087,1090,1093,1096],{"name":1085,"text":1086},"Choose a session seed and print it","Read a seed from a command-line option or generate one, and print it in the session header.",{"name":1088,"text":1089},"Derive a per-test seed","Combine the session seed with the test's node id so each test's data is stable independent of order.",{"name":1091,"text":1092},"Use faker.unique for constrained fields","Draw emails and usernames through the unique proxy and clear it per test.",{"name":1094,"text":1095},"Pin the locale","Construct Faker with an explicit locale so output does not depend on the machine.",{"name":1097,"text":1098},"Keep asserted values explicit","Pass any value the test asserts on explicitly rather than taking it from the generator.","\u002Fintegration-database-and-service-testing\u002Ftest-data-factories-and-builders\u002Fgenerating-reproducible-fake-data-with-faker",{"title":5,"description":1066},"integration-database-and-service-testing\u002Ftest-data-factories-and-builders\u002Fgenerating-reproducible-fake-data-with-faker\u002Findex","hLj2M6FzigfHVY4ds5YqhX_ofgxYiwGgw4YDK6dFnnk",1789718767486]