[{"data":1,"prerenderedAt":647},["ShallowReactive",2],{"page-\u002Fadvanced-mocking-test-doubles-in-python\u002Fdependency-injection-for-testability\u002Finjecting-fakes-vs-mocks-in-constructors\u002F":3},{"id":4,"title":5,"body":6,"description":613,"extension":614,"meta":615,"navigation":111,"path":643,"seo":644,"stem":645,"__hash__":646},"content\u002Fadvanced-mocking-test-doubles-in-python\u002Fdependency-injection-for-testability\u002Finjecting-fakes-vs-mocks-in-constructors\u002Findex.md","Injecting Fakes vs Mocks in Constructors",{"type":7,"value":8,"toc":605},"minimark",[9,34,39,72,76,83,426,430,455,459,540,544,550,559,565,569,596,601],[10,11,12,13,17,18,22,23,26,27,30,31,33],"p",{},"A ",[14,15,16],"code",{},"Mock"," is the reflexive choice for any collaborator, but it quietly fails the moment a test depends on the dependency ",[19,20,21],"em",{},"remembering"," something: a repository double that should return what was just written returns a fresh child mock instead, and the test either passes vacuously or forces you to script every call with brittle ",[14,24,25],{},"side_effect"," lists. The fix is a decision, made at the constructor seam, between a hand-written fake — a real in-memory implementation of the interface — and a ",[14,28,29],{},"unittest.mock"," double. This guide frames that trade-off: constructor injection as the seam, when a fake's coherent state beats a ",[14,32,16],{},", and how the choice pins you to either state testing or interaction testing.",[35,36,38],"h2",{"id":37},"prerequisites","Prerequisites",[40,41,42,54,63],"ul",{},[43,44,45,46,49,50,53],"li",{},"Python 3.8+ (",[14,47,48],{},"unittest.mock.AsyncMock",", modern ",[14,51,52],{},"create_autospec","). Examples target 3.11.",[43,55,56,58,59,62],{},[14,57,29],{}," from the standard library; ",[14,60,61],{},"pytest"," for the test bodies.",[43,64,65,66,71],{},"Comfort with constructor injection as a design pattern. The ",[67,68,70],"a",{"href":69},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fdependency-injection-for-testability\u002F","Dependency Injection for Testability"," overview covers the seam in depth.",[35,73,75],{"id":74},"solution","Solution",[10,77,78,79,82],{},"Expose the collaborator as a constructor parameter. Then the ",[19,80,81],{},"same"," class under test accepts a fake when state matters and a mock when only interactions matter.",[84,85,90],"pre",{"className":86,"code":87,"language":88,"meta":89,"style":89},"language-python shiki shiki-themes github-light github-dark","from typing import Protocol\nfrom unittest.mock import create_autospec\n\n\n# The contract both the real impl, the fake, and the mock satisfy.\nclass UserStore(Protocol):\n    def save(self, user_id: int, name: str) -> None: ...\n    def load(self, user_id: int) -> str | None: ...\n\n\nclass UserService:\n    # Constructor injection: the dependency is explicit and swappable.\n    # A real default keeps production wiring unchanged.\n    def __init__(self, store: UserStore) -> None:\n        self._store = store\n\n    def rename(self, user_id: int, new_name: str) -> str:\n        current = self._store.load(user_id)        # read existing state\n        if current is None:\n            raise KeyError(user_id)\n        self._store.save(user_id, new_name)        # write new state\n        return new_name\n\n\n# --- FAKE: a real in-memory implementation. Holds coherent state across calls.\nclass FakeUserStore:\n    def __init__(self) -> None:\n        self._data: dict[int, str] = {}\n\n    def save(self, user_id: int, name: str) -> None:\n        self._data[user_id] = name                 # actually stores\n\n    def load(self, user_id: int) -> str | None:\n        return self._data.get(user_id)             # returns what was stored\n\n\ndef test_rename_with_fake_state_testing():\n    fake = FakeUserStore()\n    fake.save(1, \"old\")                            # arrange real state\n    service = UserService(store=fake)              # inject the fake\n\n    service.rename(1, \"new\")\n\n    # STATE testing: assert on the fake's observable state afterward.\n    assert fake.load(1) == \"new\"\n\n\ndef test_rename_with_mock_interaction_testing():\n    # autospec binds the double to the UserStore signature (catches drift).\n    mock_store = create_autospec(UserStore, instance=True)\n    mock_store.load.return_value = \"old\"           # script the read\n    service = UserService(store=mock_store)        # inject the mock\n\n    service.rename(1, \"new\")\n\n    # INTERACTION testing: assert HOW the collaborator was used.\n    mock_store.load.assert_called_once_with(1)\n    mock_store.save.assert_called_once_with(1, \"new\")\n","python","",[14,91,92,100,106,113,118,124,130,136,142,147,152,158,164,170,176,182,187,193,199,205,211,217,223,228,233,239,245,251,257,262,268,274,279,285,291,296,301,307,313,319,325,330,336,341,347,353,358,363,369,375,381,387,393,398,403,408,414,420],{"__ignoreMap":89},[93,94,97],"span",{"class":95,"line":96},"line",1,[93,98,99],{},"from typing import Protocol\n",[93,101,103],{"class":95,"line":102},2,[93,104,105],{},"from unittest.mock import create_autospec\n",[93,107,109],{"class":95,"line":108},3,[93,110,112],{"emptyLinePlaceholder":111},true,"\n",[93,114,116],{"class":95,"line":115},4,[93,117,112],{"emptyLinePlaceholder":111},[93,119,121],{"class":95,"line":120},5,[93,122,123],{},"# The contract both the real impl, the fake, and the mock satisfy.\n",[93,125,127],{"class":95,"line":126},6,[93,128,129],{},"class UserStore(Protocol):\n",[93,131,133],{"class":95,"line":132},7,[93,134,135],{},"    def save(self, user_id: int, name: str) -> None: ...\n",[93,137,139],{"class":95,"line":138},8,[93,140,141],{},"    def load(self, user_id: int) -> str | None: ...\n",[93,143,145],{"class":95,"line":144},9,[93,146,112],{"emptyLinePlaceholder":111},[93,148,150],{"class":95,"line":149},10,[93,151,112],{"emptyLinePlaceholder":111},[93,153,155],{"class":95,"line":154},11,[93,156,157],{},"class UserService:\n",[93,159,161],{"class":95,"line":160},12,[93,162,163],{},"    # Constructor injection: the dependency is explicit and swappable.\n",[93,165,167],{"class":95,"line":166},13,[93,168,169],{},"    # A real default keeps production wiring unchanged.\n",[93,171,173],{"class":95,"line":172},14,[93,174,175],{},"    def __init__(self, store: UserStore) -> None:\n",[93,177,179],{"class":95,"line":178},15,[93,180,181],{},"        self._store = store\n",[93,183,185],{"class":95,"line":184},16,[93,186,112],{"emptyLinePlaceholder":111},[93,188,190],{"class":95,"line":189},17,[93,191,192],{},"    def rename(self, user_id: int, new_name: str) -> str:\n",[93,194,196],{"class":95,"line":195},18,[93,197,198],{},"        current = self._store.load(user_id)        # read existing state\n",[93,200,202],{"class":95,"line":201},19,[93,203,204],{},"        if current is None:\n",[93,206,208],{"class":95,"line":207},20,[93,209,210],{},"            raise KeyError(user_id)\n",[93,212,214],{"class":95,"line":213},21,[93,215,216],{},"        self._store.save(user_id, new_name)        # write new state\n",[93,218,220],{"class":95,"line":219},22,[93,221,222],{},"        return new_name\n",[93,224,226],{"class":95,"line":225},23,[93,227,112],{"emptyLinePlaceholder":111},[93,229,231],{"class":95,"line":230},24,[93,232,112],{"emptyLinePlaceholder":111},[93,234,236],{"class":95,"line":235},25,[93,237,238],{},"# --- FAKE: a real in-memory implementation. Holds coherent state across calls.\n",[93,240,242],{"class":95,"line":241},26,[93,243,244],{},"class FakeUserStore:\n",[93,246,248],{"class":95,"line":247},27,[93,249,250],{},"    def __init__(self) -> None:\n",[93,252,254],{"class":95,"line":253},28,[93,255,256],{},"        self._data: dict[int, str] = {}\n",[93,258,260],{"class":95,"line":259},29,[93,261,112],{"emptyLinePlaceholder":111},[93,263,265],{"class":95,"line":264},30,[93,266,267],{},"    def save(self, user_id: int, name: str) -> None:\n",[93,269,271],{"class":95,"line":270},31,[93,272,273],{},"        self._data[user_id] = name                 # actually stores\n",[93,275,277],{"class":95,"line":276},32,[93,278,112],{"emptyLinePlaceholder":111},[93,280,282],{"class":95,"line":281},33,[93,283,284],{},"    def load(self, user_id: int) -> str | None:\n",[93,286,288],{"class":95,"line":287},34,[93,289,290],{},"        return self._data.get(user_id)             # returns what was stored\n",[93,292,294],{"class":95,"line":293},35,[93,295,112],{"emptyLinePlaceholder":111},[93,297,299],{"class":95,"line":298},36,[93,300,112],{"emptyLinePlaceholder":111},[93,302,304],{"class":95,"line":303},37,[93,305,306],{},"def test_rename_with_fake_state_testing():\n",[93,308,310],{"class":95,"line":309},38,[93,311,312],{},"    fake = FakeUserStore()\n",[93,314,316],{"class":95,"line":315},39,[93,317,318],{},"    fake.save(1, \"old\")                            # arrange real state\n",[93,320,322],{"class":95,"line":321},40,[93,323,324],{},"    service = UserService(store=fake)              # inject the fake\n",[93,326,328],{"class":95,"line":327},41,[93,329,112],{"emptyLinePlaceholder":111},[93,331,333],{"class":95,"line":332},42,[93,334,335],{},"    service.rename(1, \"new\")\n",[93,337,339],{"class":95,"line":338},43,[93,340,112],{"emptyLinePlaceholder":111},[93,342,344],{"class":95,"line":343},44,[93,345,346],{},"    # STATE testing: assert on the fake's observable state afterward.\n",[93,348,350],{"class":95,"line":349},45,[93,351,352],{},"    assert fake.load(1) == \"new\"\n",[93,354,356],{"class":95,"line":355},46,[93,357,112],{"emptyLinePlaceholder":111},[93,359,361],{"class":95,"line":360},47,[93,362,112],{"emptyLinePlaceholder":111},[93,364,366],{"class":95,"line":365},48,[93,367,368],{},"def test_rename_with_mock_interaction_testing():\n",[93,370,372],{"class":95,"line":371},49,[93,373,374],{},"    # autospec binds the double to the UserStore signature (catches drift).\n",[93,376,378],{"class":95,"line":377},50,[93,379,380],{},"    mock_store = create_autospec(UserStore, instance=True)\n",[93,382,384],{"class":95,"line":383},51,[93,385,386],{},"    mock_store.load.return_value = \"old\"           # script the read\n",[93,388,390],{"class":95,"line":389},52,[93,391,392],{},"    service = UserService(store=mock_store)        # inject the mock\n",[93,394,396],{"class":95,"line":395},53,[93,397,112],{"emptyLinePlaceholder":111},[93,399,401],{"class":95,"line":400},54,[93,402,335],{},[93,404,406],{"class":95,"line":405},55,[93,407,112],{"emptyLinePlaceholder":111},[93,409,411],{"class":95,"line":410},56,[93,412,413],{},"    # INTERACTION testing: assert HOW the collaborator was used.\n",[93,415,417],{"class":95,"line":416},57,[93,418,419],{},"    mock_store.load.assert_called_once_with(1)\n",[93,421,423],{"class":95,"line":422},58,[93,424,425],{},"    mock_store.save.assert_called_once_with(1, \"new\")\n",[35,427,429],{"id":428},"why-this-works","Why this works",[10,431,432,433,436,437,440,441,444,445,448,449,451,452,454],{},"Constructor injection turns the dependency into a value the test supplies, so swapping a fake for a mock requires no patching and no knowledge of where the dependency is imported. A fake is a genuine implementation: its ",[14,434,435],{},"load"," returns whatever its ",[14,438,439],{},"save"," stored, so a read-modify-write flow behaves coherently and the assertion checks ",[19,442,443],{},"resulting state",". A mock returns scripted values per call and records invocations, so it naturally supports ",[19,446,447],{},"interaction testing"," — but it has no memory linking a ",[14,450,439],{}," to a later ",[14,453,435],{},", which is exactly why stateful flows want a fake.",[35,456,458],{"id":457},"edge-cases-and-failure-modes","Edge cases and failure modes",[40,460,461,478,484,503,522],{},[43,462,463,469,470,473,474,477],{},[464,465,12,466,468],"strong",{},[14,467,16],{}," makes a stateful read-after-write test lie."," ",[14,471,472],{},"mock_store.load()"," returns a child mock unrelated to any earlier ",[14,475,476],{},"save()",", so a test asserting \"the saved value is readable\" passes without exercising the contract. Use a fake when the behavior under test is the round-trip itself.",[43,479,480,483],{},[464,481,482],{},"Over-specified interaction tests turn into change detectors."," Asserting every call on a mock couples the test to the implementation; a harmless refactor (an extra cache read) breaks it. Assert only the interactions that are part of the guarantee, or switch to state testing with a fake.",[43,485,486,489,490,493,494,497,498,502],{},[464,487,488],{},"Fakes drift from the real interface silently."," A hand-written fake can fall out of sync when the real class adds a method. Define the interface as a ",[14,491,492],{},"Protocol"," (or build the fake from the same base) and consider an ",[14,495,496],{},"create_autospec(Interface, instance=True)"," smoke test so ",[67,499,501],{"href":500},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fautospec-strict-mocking\u002F","autospec strict mocking"," catches signature drift in the mock path.",[43,504,505,508,509,512,513,516,517,521],{},[464,506,507],{},"Async collaborators need awaitable doubles."," If the dependency is a coroutine interface, the mock must be an ",[14,510,511],{},"AsyncMock"," and the fake's methods must be ",[14,514,515],{},"async def","; see ",[67,518,520],{"href":519},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fdeep-dive-into-unittestmock\u002Fmock-vs-magicmock-vs-asyncmock-when-to-use-each\u002F","Mock vs MagicMock vs AsyncMock — when to use each"," for picking the class.",[43,523,524,527,528,530,531,534,535,539],{},[464,525,526],{},"Time and randomness are stateful dependencies too."," A fake clock that advances deterministically usually beats a ",[14,529,16],{}," whose ",[14,532,533],{},"now()"," returns a static value; route it through the constructor and compare with ",[67,536,538],{"href":537},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fcontrolling-time-and-randomness-in-tests\u002Ffreezing-time-with-freezegun-vs-monkeypatch\u002F","freezing time: freezegun vs monkeypatch",".",[35,541,543],{"id":542},"frequently-asked-questions","Frequently Asked Questions",[10,545,546,549],{},[464,547,548],{},"When does a hand-written fake beat a Mock?","\nUse a fake when the dependency holds state across calls — a store that must remember writes, a clock that advances — and your assertions check resulting state rather than which methods ran. A Mock returns canned values per call and does not maintain coherent internal state.",[10,551,552,555,556,539],{},[464,553,554],{},"Should I inject the dependency in the constructor or patch it?","\nInject through the constructor when you own the class. The constructor seam makes the dependency explicit, lets the test pass either a fake or a mock, and avoids brittle patch targets. Reserve patching for third-party code you cannot route through ",[464,557,558],{},"init",[10,560,561,564],{},[464,562,563],{},"How do fakes and mocks differ for verification?","\nMocks support interaction testing: assert_called_with and call_count verify how the collaborator was used. Fakes support state testing: you assert on the fake's observable state after the action. Pick the style that matches what the behavior actually guarantees.",[35,566,568],{"id":567},"related-guides","Related guides",[40,570,571,578,584,589],{},[43,572,573,574,539],{},"When constructor injection is not available, you must instead choose a patch target — see ",[67,575,577],{"href":576},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fpatching-strategies-for-complex-codebases\u002Fwhere-to-patch-understanding-mock-patch-targets\u002F","Where to Patch: Understanding mock.patch Targets",[43,579,580,581,583],{},"Bind the mock path to the real signature with ",[67,582,501],{"href":500}," so fakes and mocks share one contract.",[43,585,586,587,539],{},"Choosing the mock class for the dependency is covered in ",[67,588,520],{"href":519},[43,590,591,592,539],{},"For collaborators that are network calls, a fake transport often beats a mock — see ",[67,593,595],{"href":594},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fmocking-network-and-http-calls\u002F","Mocking Network and HTTP Calls",[10,597,598,599],{},"← Back to ",[67,600,70],{"href":69},[602,603,604],"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":89,"searchDepth":102,"depth":102,"links":606},[607,608,609,610,611,612],{"id":37,"depth":102,"text":38},{"id":74,"depth":102,"text":75},{"id":428,"depth":102,"text":429},{"id":457,"depth":102,"text":458},{"id":542,"depth":102,"text":543},{"id":567,"depth":102,"text":568},"Decide between hand-written fakes and unittest.mock doubles at the constructor seam: state vs interaction testing, stateful behavior, and verification trade-offs.","md",{"slug":616,"type":617,"breadcrumb":618,"datePublished":619,"dateModified":619,"faq":620,"howto":627},"injecting-fakes-vs-mocks-in-constructors","long_tail","Fakes vs Mocks","2026-06-18",[621,623,625],{"q":548,"a":622},"Use a fake when the dependency holds state across calls — a store that must remember writes, a clock that advances — and your assertions check resulting state rather than which methods ran. A Mock returns canned values per call and does not maintain coherent internal state.",{"q":554,"a":624},"Inject through the constructor when you own the class. The constructor seam makes the dependency explicit, lets the test pass either a fake or a mock, and avoids brittle patch targets. Reserve patching for third-party code you cannot route through __init__.",{"q":563,"a":626},"Mocks support interaction testing: assert_called_with and call_count verify how the collaborator was used. Fakes support state testing: you assert on the fake's observable state after the action. Pick the style that matches what the behavior actually guarantees.",{"name":628,"description":629,"steps":630},"How to inject fakes versus mocks through a constructor","Expose the dependency as a constructor parameter, then inject a fake for stateful state testing or a mock for interaction testing.",[631,634,637,640],{"name":632,"text":633},"Expose the seam","Accept the collaborator as a constructor parameter with a default real implementation so production wiring is unchanged.",{"name":635,"text":636},"Define a fake for stateful behavior","Write a small in-memory class implementing the same interface when the dependency holds state the assertions depend on.",{"name":638,"text":639},"Inject a mock for interaction checks","Pass a Mock or autospec double when the test only needs to confirm how the collaborator was called.",{"name":641,"text":642},"Assert on the right surface","Assert on the fake's resulting state for state tests, or on mock call assertions for interaction tests.","\u002Fadvanced-mocking-test-doubles-in-python\u002Fdependency-injection-for-testability\u002Finjecting-fakes-vs-mocks-in-constructors",{"title":5,"description":613},"advanced-mocking-test-doubles-in-python\u002Fdependency-injection-for-testability\u002Finjecting-fakes-vs-mocks-in-constructors\u002Findex","08J2s4UhUAliFfaianhla2OFw1Xst39LWce4vt6xutw",1781793487880]