[{"data":1,"prerenderedAt":1144},["ShallowReactive",2],{"page-\u002Fadvanced-mocking-test-doubles-in-python\u002Fautospec-strict-mocking\u002Fcreate-autospec-vs-patch-autospec-true\u002F":3},{"id":4,"title":5,"body":6,"description":1108,"extension":1109,"meta":1110,"navigation":95,"path":1140,"seo":1141,"stem":1142,"__hash__":1143},"content\u002Fadvanced-mocking-test-doubles-in-python\u002Fautospec-strict-mocking\u002Fcreate-autospec-vs-patch-autospec-true\u002Findex.md","create_autospec vs patch(autospec=True)",{"type":7,"value":8,"toc":1097},"minimark",[9,22,27,54,58,61,216,241,244,269,398,402,431,563,567,578,581,596,639,649,653,659,662,724,731,738,742,745,781,799,817,906,910,986,1003,1007,1019,1034,1040,1053,1057,1087,1093],[10,11,12,13,17,18,21],"p",{},"Both calls produce a double bound to a real interface, and teams routinely use them interchangeably until a test starts passing a class mock where an instance was expected. They are not alternatives to each other — they answer different questions. ",[14,15,16],"code",{},"patch(autospec=True)"," replaces a name the code under test looks up; ",[14,19,20],{},"create_autospec"," hands you an object to pass in yourself.",[23,24,26],"h2",{"id":25},"prerequisites","Prerequisites",[28,29,30,45],"ul",{},[31,32,33,37,38,40,41,44],"li",{},[34,35,36],"strong",{},"Python 3.8+","; ",[14,39,20],{}," gained proper async support in 3.8 and ",[14,42,43],{},"instance=True"," behaves as described throughout.",[31,46,47,48,53],{},"The spec model from ",[49,50,52],"a",{"href":51},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fautospec-strict-mocking\u002F","autospec and strict mocking",".",[23,55,57],{"id":56},"solution","Solution",[10,59,60],{},"Choose by how the collaborator reaches the code, not by preference.",[62,63,68],"pre",{"className":64,"code":65,"language":66,"meta":67,"style":67},"language-python shiki shiki-themes github-light github-dark","from unittest.mock import create_autospec, patch\nfrom myapp.gateway import PaymentGateway\nfrom myapp import orders            # orders.py does: from myapp.gateway import PaymentGateway\n\n\n# 1. The code looks the name up itself -> patch it where it is used.\ndef test_charge_calls_the_gateway():\n    with patch.object(orders, \"PaymentGateway\", autospec=True) as MockGateway:\n        instance = MockGateway.return_value       # what orders.py will construct\n        instance.charge.return_value = {\"id\": \"ch_1\"}\n\n        orders.place_order(amount=100)\n\n        instance.charge.assert_called_once_with(amount=100, currency=\"EUR\")\n\n\n# 2. The code receives the collaborator -> build the double and inject it.\ndef test_charge_with_injected_gateway():\n    gateway = create_autospec(PaymentGateway, instance=True)   # an INSTANCE double\n    gateway.charge.return_value = {\"id\": \"ch_1\"}\n\n    service = OrderService(gateway=gateway)                    # constructor injection\n    service.place_order(amount=100)\n\n    gateway.charge.assert_called_once_with(amount=100, currency=\"EUR\")\n","python","",[14,69,70,78,84,90,97,102,108,114,120,126,132,137,143,148,154,159,164,170,176,182,188,193,199,205,210],{"__ignoreMap":67},[71,72,75],"span",{"class":73,"line":74},"line",1,[71,76,77],{},"from unittest.mock import create_autospec, patch\n",[71,79,81],{"class":73,"line":80},2,[71,82,83],{},"from myapp.gateway import PaymentGateway\n",[71,85,87],{"class":73,"line":86},3,[71,88,89],{},"from myapp import orders            # orders.py does: from myapp.gateway import PaymentGateway\n",[71,91,93],{"class":73,"line":92},4,[71,94,96],{"emptyLinePlaceholder":95},true,"\n",[71,98,100],{"class":73,"line":99},5,[71,101,96],{"emptyLinePlaceholder":95},[71,103,105],{"class":73,"line":104},6,[71,106,107],{},"# 1. The code looks the name up itself -> patch it where it is used.\n",[71,109,111],{"class":73,"line":110},7,[71,112,113],{},"def test_charge_calls_the_gateway():\n",[71,115,117],{"class":73,"line":116},8,[71,118,119],{},"    with patch.object(orders, \"PaymentGateway\", autospec=True) as MockGateway:\n",[71,121,123],{"class":73,"line":122},9,[71,124,125],{},"        instance = MockGateway.return_value       # what orders.py will construct\n",[71,127,129],{"class":73,"line":128},10,[71,130,131],{},"        instance.charge.return_value = {\"id\": \"ch_1\"}\n",[71,133,135],{"class":73,"line":134},11,[71,136,96],{"emptyLinePlaceholder":95},[71,138,140],{"class":73,"line":139},12,[71,141,142],{},"        orders.place_order(amount=100)\n",[71,144,146],{"class":73,"line":145},13,[71,147,96],{"emptyLinePlaceholder":95},[71,149,151],{"class":73,"line":150},14,[71,152,153],{},"        instance.charge.assert_called_once_with(amount=100, currency=\"EUR\")\n",[71,155,157],{"class":73,"line":156},15,[71,158,96],{"emptyLinePlaceholder":95},[71,160,162],{"class":73,"line":161},16,[71,163,96],{"emptyLinePlaceholder":95},[71,165,167],{"class":73,"line":166},17,[71,168,169],{},"# 2. The code receives the collaborator -> build the double and inject it.\n",[71,171,173],{"class":73,"line":172},18,[71,174,175],{},"def test_charge_with_injected_gateway():\n",[71,177,179],{"class":73,"line":178},19,[71,180,181],{},"    gateway = create_autospec(PaymentGateway, instance=True)   # an INSTANCE double\n",[71,183,185],{"class":73,"line":184},20,[71,186,187],{},"    gateway.charge.return_value = {\"id\": \"ch_1\"}\n",[71,189,191],{"class":73,"line":190},21,[71,192,96],{"emptyLinePlaceholder":95},[71,194,196],{"class":73,"line":195},22,[71,197,198],{},"    service = OrderService(gateway=gateway)                    # constructor injection\n",[71,200,202],{"class":73,"line":201},23,[71,203,204],{},"    service.place_order(amount=100)\n",[71,206,208],{"class":73,"line":207},24,[71,209,96],{"emptyLinePlaceholder":95},[71,211,213],{"class":73,"line":212},25,[71,214,215],{},"    gateway.charge.assert_called_once_with(amount=100, currency=\"EUR\")\n",[10,217,218,220,221,224,225,229,230,233,234,236,237,240],{},[14,219,43],{}," is the detail that causes the most confusion. Without it, ",[14,222,223],{},"create_autospec(PaymentGateway)"," returns a ",[226,227,228],"em",{},"class"," mock: callable, with ",[14,231,232],{},"return_value"," being the instance double. Pass that where an instance is expected and every attribute access resolves on the wrong object — the test still passes, and it asserts nothing about the real interface. With ",[14,235,43],{},", the double is the instance and calling it raises ",[14,238,239],{},"TypeError",", which turns the mistake into a failure.",[10,242,243],{},"Both forms enforce signatures, and that enforcement is the whole reason to use either:",[62,245,247],{"className":64,"code":246,"language":66,"meta":67,"style":67},"gateway = create_autospec(PaymentGateway, instance=True)\ngateway.charge(amount=100, currency=\"EUR\")          # fine\ngateway.charge(100, \"EUR\", retries=3)               # TypeError: unexpected keyword\ngateway.refund_all()                                # AttributeError: no such method\n",[14,248,249,254,259,264],{"__ignoreMap":67},[71,250,251],{"class":73,"line":74},[71,252,253],{},"gateway = create_autospec(PaymentGateway, instance=True)\n",[71,255,256],{"class":73,"line":80},[71,257,258],{},"gateway.charge(amount=100, currency=\"EUR\")          # fine\n",[71,260,261],{"class":73,"line":86},[71,262,263],{},"gateway.charge(100, \"EUR\", retries=3)               # TypeError: unexpected keyword\n",[71,265,266],{"class":73,"line":92},[71,267,268],{},"gateway.refund_all()                                # AttributeError: no such method\n",[270,271,274,394],"figure",{"className":272},[273],"diagram",[275,276,283,284,283,288,283,292,283,300,283,309,283,318,283,324,283,328,283,332,283,337,283,342,283,345,283,349,283,352,283,356,283,359,283,361,283,364,283,368,283,371,283,373,283,376,283,380,283,383,283,387,283,391],"svg",{"viewBox":277,"role":278,"ariaLabelledBy":279,"xmlns":282},"0 0 760 270","img",[280,281],"autospecforms-t","autospecforms-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[285,286,287],"title",{"id":280},"Four ways to build a specced double",[289,290,291],"desc",{"id":281},"A table comparing create_autospec on a class, create_autospec with instance True, patch with autospec True, and Mock with a spec argument, across what each returns, whether signatures are enforced, and where each belongs.",[293,294],"rect",{"x":295,"y":295,"width":296,"height":297,"rx":298,"fill":299},"0","760","270","14","#fffdf8",[301,302,287],"text",{"x":303,"y":304,"textAnchor":305,"fontSize":306,"fontWeight":307,"fill":308},"380","30","middle","15","700","#3d405b",[293,310],{"x":311,"y":312,"width":313,"height":314,"rx":315,"fill":316,"stroke":308,"strokeWidth":317},"24","52","712","40","10","#f2cc8f","1.5",[301,319,323],{"x":320,"y":321,"fontSize":322,"fontWeight":307,"fill":308},"38","76","12","Criterion",[301,325,327],{"x":326,"y":321,"textAnchor":305,"fontSize":322,"fontWeight":307,"fill":308},"390","Returns",[301,329,331],{"x":330,"y":321,"textAnchor":305,"fontSize":322,"fontWeight":307,"fill":308},"620","Signatures",[293,333],{"x":311,"y":334,"width":313,"height":314,"fill":335,"stroke":308,"strokeWidth":336},"92","#f4f1de","1",[301,338,341],{"x":320,"y":339,"fontSize":340,"fill":308},"116","11.5","create_autospec(Cls)",[301,343,344],{"x":326,"y":339,"textAnchor":305,"fontSize":340,"fill":308},"a class mock",[301,346,348],{"x":330,"y":339,"textAnchor":305,"fontSize":340,"fill":347},"#2a5f49","enforced",[293,350],{"x":311,"y":351,"width":313,"height":314,"fill":299,"stroke":308,"strokeWidth":336},"132",[301,353,355],{"x":320,"y":354,"fontSize":340,"fill":308},"156","create_autospec(Cls, instance=True)",[301,357,358],{"x":326,"y":354,"textAnchor":305,"fontSize":340,"fill":308},"an instance double",[301,360,348],{"x":330,"y":354,"textAnchor":305,"fontSize":340,"fill":347},[293,362],{"x":311,"y":363,"width":313,"height":314,"fill":335,"stroke":308,"strokeWidth":336},"172",[301,365,367],{"x":320,"y":366,"fontSize":340,"fill":308},"196","patch(target, autospec=True)",[301,369,370],{"x":326,"y":366,"textAnchor":305,"fontSize":340,"fill":308},"installs a class mock",[301,372,348],{"x":330,"y":366,"textAnchor":305,"fontSize":340,"fill":347},[293,374],{"x":311,"y":375,"width":313,"height":314,"fill":299,"stroke":308,"strokeWidth":336},"212",[301,377,379],{"x":320,"y":378,"fontSize":340,"fill":308},"236","Mock(spec=Cls)",[301,381,382],{"x":326,"y":378,"textAnchor":305,"fontSize":340,"fill":308},"a shallow double",[301,384,386],{"x":330,"y":378,"textAnchor":305,"fontSize":340,"fill":385},"#8f3d22","names only",[73,388],{"x1":389,"y1":312,"x2":389,"y2":390,"stroke":308,"strokeWidth":336},"274","252",[73,392],{"x1":393,"y1":312,"x2":393,"y2":390,"stroke":308,"strokeWidth":336},"505",[395,396,397],"figcaption",{},"Only the first three copy real signatures; Mock(spec=...) checks attribute names and lets any call shape through.",[23,399,401],{"id":400},"why-this-works","Why this works",[10,403,404,406,407,410,411,414,415,418,419,422,423,426,427,430],{},[14,405,20],{}," walks the spec object with ",[14,408,409],{},"dir()",", and for every callable it finds it copies the real ",[14,412,413],{},"__signature__"," onto the child mock, recursing into nested attributes. The resulting double therefore rejects both unknown names and wrong call shapes. ",[14,416,417],{},"patch(..., autospec=True)"," does exactly the same construction, then performs the additional work of ",[14,420,421],{},"setattr","-ing the double over the target and restoring the original in a ",[14,424,425],{},"finally",". The ",[14,428,429],{},"instance"," flag decides which end of the class\u002Finstance pair you receive: a class mock is callable and returns the instance double, and an instance double is not callable at all.",[270,432,434,557],{"className":433},[273],[275,435,283,440,283,443,283,446,283,475,283,478,283,480,283,485,283,489,283,495,283,497,283,500,283,503,283,506,283,510,283,513,283,519,283,524,283,529,283,534,283,537,283,541,283,548,283,552],{"viewBox":436,"role":278,"ariaLabelledBy":437,"xmlns":282},"0 0 760 376",[438,439],"autospecpair-t","autospecpair-d",[285,441,442],{"id":438},"The class mock and the instance double",[289,444,445],{"id":439},"A sequence diagram with three lanes: the test, the class mock produced by patch, and the instance double. The test configures the instance double through return_value, the code under test constructs the class which yields that same instance, and the assertions afterwards are made against the instance rather than the class.",[447,448,449,450,449,463,449,469,283],"defs",{},"\n    ",[451,452,459],"marker",{"id":453,"viewBox":454,"refX":455,"refY":456,"markerWidth":457,"markerHeight":457,"orient":458},"autospecpair-a","0 0 10 10","9","5","7","auto-start-reverse",[460,461],"path",{"d":462,"fill":308},"M0 0 L10 5 L0 10 z",[451,464,466],{"id":465,"viewBox":454,"refX":455,"refY":456,"markerWidth":457,"markerHeight":457,"orient":458},"autospecpair-c",[460,467],{"d":462,"fill":468},"#e07a5f",[451,470,472],{"id":471,"viewBox":454,"refX":455,"refY":456,"markerWidth":457,"markerHeight":457,"orient":458},"autospecpair-s",[460,473],{"d":462,"fill":474},"#81b29a",[293,476],{"x":295,"y":295,"width":296,"height":477,"rx":298,"fill":299},"376",[301,479,442],{"x":303,"y":304,"textAnchor":305,"fontSize":306,"fontWeight":307,"fill":308},[293,481],{"x":482,"y":312,"width":483,"height":314,"rx":315,"fill":335,"stroke":308,"strokeWidth":484},"34","220","1.8",[301,486,488],{"x":487,"y":321,"textAnchor":305,"fontSize":322,"fontWeight":307,"fill":308},"144","test",[73,490],{"x1":487,"y1":491,"x2":487,"y2":492,"stroke":308,"strokeWidth":493,"strokeDashArray":494},"98","360","1.2",[456,456],[293,496],{"x":297,"y":312,"width":483,"height":314,"rx":315,"fill":335,"stroke":308,"strokeWidth":484},[301,498,499],{"x":303,"y":321,"textAnchor":305,"fontSize":322,"fontWeight":307,"fill":308},"class mock",[73,501],{"x1":303,"y1":491,"x2":303,"y2":492,"stroke":308,"strokeWidth":493,"strokeDashArray":502},[456,456],[293,504],{"x":505,"y":312,"width":483,"height":314,"rx":315,"fill":335,"stroke":308,"strokeWidth":484},"506",[301,507,509],{"x":508,"y":321,"textAnchor":305,"fontSize":322,"fontWeight":307,"fill":308},"616","instance double",[73,511],{"x1":508,"y1":491,"x2":508,"y2":492,"stroke":308,"strokeWidth":493,"strokeDashArray":512},[456,456],[73,514],{"x1":515,"y1":516,"x2":517,"y2":516,"stroke":308,"strokeWidth":484,"markerEnd":518},"152","126","372","url(#autospecpair-a)",[301,520,417],{"x":521,"y":522,"textAnchor":305,"fontSize":523,"fill":308},"262","117","11",[73,525],{"x1":526,"y1":527,"x2":528,"y2":527,"stroke":308,"strokeWidth":484,"markerEnd":518},"388","180","608",[301,530,533],{"x":531,"y":532,"textAnchor":305,"fontSize":523,"fill":308},"498","171","return_value is the instance",[73,535],{"x1":515,"y1":536,"x2":528,"y2":536,"stroke":308,"strokeWidth":484,"markerEnd":518},"234",[301,538,540],{"x":303,"y":539,"textAnchor":305,"fontSize":523,"fill":308},"225","configure charge()",[73,542],{"x1":528,"y1":543,"x2":515,"y2":543,"stroke":474,"strokeWidth":484,"strokeDashArray":544,"markerEnd":547},"288",[545,546],"6","4","url(#autospecpair-s)",[301,549,551],{"x":303,"y":550,"textAnchor":305,"fontSize":523,"fill":308},"279","assertions land here",[301,553,556],{"x":303,"y":554,"textAnchor":305,"fontSize":523,"fontStyle":555,"fill":308},"364","italic","Passing the class mock where an instance is expected is the classic silent-pass bug.",[395,558,559,560,562],{},"With patch you always configure and assert on ",[14,561,232],{},"; with instance=True there is no class mock in the picture at all.",[23,564,566],{"id":565},"making-the-choice-mechanical","Making the choice mechanical",[10,568,569,570,573,574,577],{},"The decision follows from one question: does the code under test ",[226,571,572],{},"obtain"," the collaborator, or ",[226,575,576],{},"receive"," it?",[10,579,580],{},"Code that imports a name and constructs it must be patched, because there is no seam to inject through. Code that takes the collaborator as a constructor or function argument should be given a double directly — patching it would be reaching past a seam that already exists, and the resulting test is harder to read and more fragile.",[10,582,583,584,588,589,591,592,595],{},"That framing also explains why a codebase using ",[49,585,587],{"href":586},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fdependency-injection-for-testability\u002F","dependency injection for testability"," needs very little patching: the seams make ",[14,590,20],{}," sufficient almost everywhere, and ",[14,593,594],{},"patch"," is reserved for third-party globals and module-level singletons that cannot be injected.",[62,597,599],{"className":64,"code":598,"language":66,"meta":67,"style":67},"# No seam: the function reaches for the name itself.\ndef send_receipt(order):\n    client = SmtpClient(host=settings.SMTP_HOST)     # must be patched\n    client.send(order.email, render(order))\n\n# Seam present: the collaborator arrives as an argument.\ndef send_receipt(order, client):                     # inject a create_autospec double\n    client.send(order.email, render(order))\n",[14,600,601,606,611,616,621,625,630,635],{"__ignoreMap":67},[71,602,603],{"class":73,"line":74},[71,604,605],{},"# No seam: the function reaches for the name itself.\n",[71,607,608],{"class":73,"line":80},[71,609,610],{},"def send_receipt(order):\n",[71,612,613],{"class":73,"line":86},[71,614,615],{},"    client = SmtpClient(host=settings.SMTP_HOST)     # must be patched\n",[71,617,618],{"class":73,"line":92},[71,619,620],{},"    client.send(order.email, render(order))\n",[71,622,623],{"class":73,"line":99},[71,624,96],{"emptyLinePlaceholder":95},[71,626,627],{"class":73,"line":104},[71,628,629],{},"# Seam present: the collaborator arrives as an argument.\n",[71,631,632],{"class":73,"line":110},[71,633,634],{},"def send_receipt(order, client):                     # inject a create_autospec double\n",[71,636,637],{"class":73,"line":116},[71,638,620],{},[10,640,641,642,644,645,53],{},"Where both are possible, prefer injection. ",[14,643,594],{}," binds the test to the module layout — move the import and the test breaks even though the behaviour did not — while an injected double binds only to the interface, which is the thing the test is actually about. That coupling to module structure is the same one described in ",[49,646,648],{"href":647},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fpatching-strategies-for-complex-codebases\u002Fwhere-to-patch-understanding-mock-patch-targets\u002F","where to patch",[23,650,652],{"id":651},"cost-and-caching","Cost and caching",[10,654,655,656,53],{},"Autospec is not free. Building the double introspects the class and every attribute it exposes, and for a large class — an ORM model, a client with a hundred methods, a module object — that introspection can take milliseconds, paid per construction. In a suite that builds the same double in five hundred tests, the cost is visible in ",[14,657,658],{},"--durations",[10,660,661],{},"Two mitigations, in order of preference. Build the double once in a session-scoped fixture and reset it per test, which keeps the introspection cost singular:",[62,663,665],{"className":64,"code":664,"language":66,"meta":67,"style":67},"import pytest\nfrom unittest.mock import create_autospec\nfrom myapp.gateway import PaymentGateway\n\n@pytest.fixture(scope=\"session\")\ndef _gateway_template():\n    return create_autospec(PaymentGateway, instance=True)   # introspected once\n\n@pytest.fixture\ndef gateway(_gateway_template):\n    _gateway_template.reset_mock(return_value=True, side_effect=True)\n    return _gateway_template                                 # cheap per test\n",[14,666,667,672,677,681,685,690,695,700,704,709,714,719],{"__ignoreMap":67},[71,668,669],{"class":73,"line":74},[71,670,671],{},"import pytest\n",[71,673,674],{"class":73,"line":80},[71,675,676],{},"from unittest.mock import create_autospec\n",[71,678,679],{"class":73,"line":86},[71,680,83],{},[71,682,683],{"class":73,"line":92},[71,684,96],{"emptyLinePlaceholder":95},[71,686,687],{"class":73,"line":99},[71,688,689],{},"@pytest.fixture(scope=\"session\")\n",[71,691,692],{"class":73,"line":104},[71,693,694],{},"def _gateway_template():\n",[71,696,697],{"class":73,"line":110},[71,698,699],{},"    return create_autospec(PaymentGateway, instance=True)   # introspected once\n",[71,701,702],{"class":73,"line":116},[71,703,96],{"emptyLinePlaceholder":95},[71,705,706],{"class":73,"line":122},[71,707,708],{},"@pytest.fixture\n",[71,710,711],{"class":73,"line":128},[71,712,713],{},"def gateway(_gateway_template):\n",[71,715,716],{"class":73,"line":134},[71,717,718],{},"    _gateway_template.reset_mock(return_value=True, side_effect=True)\n",[71,720,721],{"class":73,"line":139},[71,722,723],{},"    return _gateway_template                                 # cheap per test\n",[10,725,726,727,730],{},"Or spec a narrower object: if the code only uses two methods, autospec a small ",[14,728,729],{},"Protocol"," describing those two rather than the full class. That is faster, and it documents the actual dependency — a class with a hundred methods being autospecced usually means the code depends on far less than it appears to.",[10,732,733,734,737],{},"Do not reach for ",[14,735,736],{},"Mock(spec=...)"," as the cheap option. It skips signature copying, which is the property that makes autospec worth having; the saving is real but it buys back exactly the failure mode you were preventing.",[23,739,741],{"id":740},"verifying-the-double-really-is-specced","Verifying the double really is specced",[10,743,744],{},"A double that silently lost its spec is worse than a plain mock, because the test reads as strict and behaves as permissive. Two one-line checks catch it.",[62,746,748],{"className":64,"code":747,"language":66,"meta":67,"style":67},"from unittest.mock import create_autospec\nfrom myapp.gateway import PaymentGateway\n\ngateway = create_autospec(PaymentGateway, instance=True, spec_set=True)\n\nassert gateway._spec_class is PaymentGateway      # bound to the real class\nassert not callable(gateway)                      # it is an instance, not a class mock\n",[14,749,750,754,758,762,767,771,776],{"__ignoreMap":67},[71,751,752],{"class":73,"line":74},[71,753,676],{},[71,755,756],{"class":73,"line":80},[71,757,83],{},[71,759,760],{"class":73,"line":86},[71,761,96],{"emptyLinePlaceholder":95},[71,763,764],{"class":73,"line":92},[71,765,766],{},"gateway = create_autospec(PaymentGateway, instance=True, spec_set=True)\n",[71,768,769],{"class":73,"line":99},[71,770,96],{"emptyLinePlaceholder":95},[71,772,773],{"class":73,"line":104},[71,774,775],{},"assert gateway._spec_class is PaymentGateway      # bound to the real class\n",[71,777,778],{"class":73,"line":110},[71,779,780],{},"assert not callable(gateway)                      # it is an instance, not a class mock\n",[10,782,783,784,787,788,791,792,795,796,798],{},"The second assertion is the one worth keeping in a shared fixture: ",[14,785,786],{},"callable(double)"," is ",[14,789,790],{},"True"," for a class mock and ",[14,793,794],{},"False"," for an instance double, so it fails loudly the day someone drops ",[14,797,43],{}," while refactoring.",[10,800,801,802,804,805,808,809,812,813,816],{},"At review time, three signals distinguish a real autospec double from a decorative one. The spec argument is a real imported class rather than a string; ",[14,803,43],{}," is present whenever the code expects an instance; and no test attaches attributes the class does not declare — a line like ",[14,806,807],{},"double.internal_cache = {}"," next to a ",[14,810,811],{},"spec_set"," double raises immediately, and next to a plain ",[14,814,815],{},"spec"," double silently creates state that the real object never has.",[270,818,820,903],{"className":819},[273],[275,821,283,826,283,829,283,832,283,839,283,842,283,844,283,849,283,855,283,859,283,865,283,868,283,872,283,875,283,878,283,881,283,885,283,888,283,892,283,895,283,899],{"viewBox":822,"role":278,"ariaLabelledBy":823,"xmlns":282},"0 0 760 154",[824,825],"speccheck-t","speccheck-d",[285,827,828],{"id":824},"Three checks that a double still matches its class",[289,830,831],{"id":825},"A left-to-right check sequence: confirm the spec class is the real one, confirm callable-ness matches instance or class expectations, call with a wrong signature to confirm TypeError, and confirm an unknown attribute raises.",[447,833,449,834,283],{},[451,835,837],{"id":836,"viewBox":454,"refX":455,"refY":456,"markerWidth":457,"markerHeight":457,"orient":458},"speccheck-a",[460,838],{"d":462,"fill":316},[293,840],{"x":295,"y":295,"width":296,"height":841,"rx":298,"fill":299},"154",[301,843,828],{"x":303,"y":304,"textAnchor":305,"fontSize":306,"fontWeight":307,"fill":308},[293,845],{"x":846,"y":847,"width":848,"height":321,"rx":322,"fill":335,"stroke":316,"strokeWidth":484},"26","62","142",[301,850,854],{"x":851,"y":852,"textAnchor":305,"fontSize":853,"fontWeight":307,"fill":308},"97","96","12.5","spec class",[301,856,858],{"x":851,"y":857,"textAnchor":305,"fontSize":523,"fill":308},"113","bound to the real class",[73,860],{"x1":861,"y1":862,"x2":863,"y2":862,"stroke":316,"strokeWidth":484,"markerEnd":864},"176","100","208","url(#speccheck-a)",[293,866],{"x":867,"y":847,"width":848,"height":321,"rx":322,"fill":299,"stroke":316,"strokeWidth":484},"214",[301,869,871],{"x":870,"y":852,"textAnchor":305,"fontSize":853,"fontWeight":307,"fill":308},"286","callable?",[301,873,874],{"x":870,"y":857,"textAnchor":305,"fontSize":523,"fill":308},"instance vs class mock",[73,876],{"x1":554,"y1":862,"x2":877,"y2":862,"stroke":316,"strokeWidth":484,"markerEnd":864},"396",[293,879],{"x":880,"y":847,"width":848,"height":321,"rx":322,"fill":335,"stroke":316,"strokeWidth":484},"403",[301,882,884],{"x":883,"y":852,"textAnchor":305,"fontSize":853,"fontWeight":307,"fill":308},"474","wrong signature",[301,886,887],{"x":883,"y":857,"textAnchor":305,"fontSize":523,"fill":308},"must raise TypeError",[73,889],{"x1":890,"y1":862,"x2":891,"y2":862,"stroke":316,"strokeWidth":484,"markerEnd":864},"552","584",[293,893],{"x":894,"y":847,"width":848,"height":321,"rx":322,"fill":299,"stroke":316,"strokeWidth":484},"592",[301,896,898],{"x":897,"y":852,"textAnchor":305,"fontSize":853,"fontWeight":307,"fill":308},"663","unknown attr",[301,900,902],{"x":897,"y":857,"textAnchor":305,"fontSize":901,"fill":308},"10.0","must raise AttributeError",[395,904,905],{},"All four are cheap assertions; together they prove the double is enforcing a contract rather than pretending to.",[23,907,909],{"id":908},"edge-cases-and-failure-modes","Edge cases and failure modes",[28,911,912,924,938,948,964,978],{},[31,913,914,919,920,923],{},[34,915,916,918],{},[14,917,16],{}," on a function target replaces the function",", so the mock receives the same arguments the function would, including ",[14,921,922],{},"self"," for unbound methods — a frequent surprise when patching methods on a class rather than on an instance.",[31,925,926,929,930,933,934,53],{},[34,927,928],{},"Properties become plain attributes"," under both forms; observing access requires ",[14,931,932],{},"PropertyMock",", as covered in ",[49,935,937],{"href":936},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fautospec-strict-mocking\u002Fmocking-properties-and-class-attributes-with-autospec\u002F","mocking properties and class attributes with autospec",[31,939,940,947],{},[34,941,942,943,946],{},"Attributes created only in ",[14,944,945],{},"__init__"," are invisible"," to introspection of the class, so the double lacks them until you assign them explicitly.",[31,949,950,960,961,963],{},[34,951,952,955,956,959],{},[14,953,954],{},"autospec=True"," follows ",[14,957,958],{},"__getattr__","-based dynamic APIs poorly."," A client that generates methods at runtime has nothing for ",[14,962,409],{}," to find, and the double rejects every call the real object would accept.",[31,965,966,973,974,977],{},[34,967,968,969,972],{},"Async methods produce ",[14,970,971],{},"AsyncMock"," children automatically"," on Python 3.8+, but only when the spec really declares them ",[14,975,976],{},"async def","; a sync method returning a coroutine is not detected.",[31,979,980,985],{},[34,981,982,984],{},[14,983,811],{}," is orthogonal and worth adding."," Both forms accept it, and it closes the write path so a typo'd attribute assignment fails instead of silently creating state.",[10,987,988,989,991,992,995,996,999,1000,1002],{},"A note on decorator stacking, since ",[14,990,594],{}," is most often used as one. Multiple ",[14,993,994],{},"@patch"," decorators apply bottom-up, so the mock arguments arrive in the reverse of the order they are written, and mixing them with ",[14,997,998],{},"pytest"," fixtures means the mocks come first in the signature. That ordering is the single most common source of a test where two doubles are configured on the wrong objects — and because both are mocks, nothing fails until an assertion checks the wrong one. Using ",[14,1001,594],{}," as a context manager inside the test body removes the ordering question entirely, at the cost of one indent level, which is usually a good trade for anything beyond two patches.",[23,1004,1006],{"id":1005},"frequently-asked-questions","Frequently Asked Questions",[10,1008,1009,1012,1013,1015,1016,1018],{},[34,1010,1011],{},"Are create_autospec and patch(autospec=True) the same thing?","\nThey build the same kind of double. The difference is placement: ",[14,1014,20],{}," returns an object you pass somewhere yourself, while ",[14,1017,16],{}," builds one and installs it over a named target for the duration of the block.",[10,1020,1021,1024,1025,1027,1028,1030,1031,1033],{},[34,1022,1023],{},"When do I need instance=True?","\nWhen the code under test receives an instance rather than a class. ",[14,1026,341],{}," returns a callable class mock whose ",[14,1029,232],{}," is the instance double; ",[14,1032,355],{}," returns the instance double directly and refuses to be called.",[10,1035,1036,1039],{},[34,1037,1038],{},"Why is autospec slow on large classes?","\nBecause it introspects the whole object graph at creation time, recursively specced for each attribute. On a class with hundreds of members, or one whose attribute access triggers imports, that cost is paid per call — cache the spec in a fixture rather than rebuilding it per test.",[10,1041,1042,1045,1046,1048,1049,1052],{},[34,1043,1044],{},"Does autospec catch wrong keyword arguments?","\nYes. The double copies the real signature, so a call with an unknown keyword or the wrong arity raises ",[14,1047,239],{}," at call time, which a plain ",[14,1050,1051],{},"Mock"," accepts silently.",[23,1054,1056],{"id":1055},"related","Related",[28,1058,1059,1065,1071,1080],{},[31,1060,1061,1064],{},[49,1062,1063],{"href":51},"Autospec and strict mocking"," — the contract enforcement both forms provide.",[31,1066,1067,1070],{},[49,1068,1069],{"href":647},"Where to patch: understanding mock.patch targets"," — choosing the target when patching is the right answer.",[31,1072,1073,1076,1077,1079],{},[49,1074,1075],{"href":586},"Dependency injection for testability"," — the seams that make ",[14,1078,20],{}," sufficient.",[31,1081,1082,1086],{},[49,1083,1085],{"href":1084},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fdeep-dive-into-unittestmock\u002Fassert-called-with-vs-call-args-list\u002F","assert_called_with vs call_args_list"," — asserting on the double once it is in place.",[10,1088,1089,1090],{},"← Back to ",[49,1091,1092],{"href":51},"Autospec & Strict Mocking",[1094,1095,1096],"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":67,"searchDepth":80,"depth":80,"links":1098},[1099,1100,1101,1102,1103,1104,1105,1106,1107],{"id":25,"depth":80,"text":26},{"id":56,"depth":80,"text":57},{"id":400,"depth":80,"text":401},{"id":565,"depth":80,"text":566},{"id":651,"depth":80,"text":652},{"id":740,"depth":80,"text":741},{"id":908,"depth":80,"text":909},{"id":1005,"depth":80,"text":1006},{"id":1055,"depth":80,"text":1056},"Compare create_autospec and patch(autospec=True) in unittest.mock: what each binds, when instance=True matters, and why one is for injection and the other for patching.","md",{"slug":1111,"type":1112,"breadcrumb":1113,"datePublished":1114,"dateModified":1114,"faq":1115,"howto":1124},"create-autospec-vs-patch-autospec-true","article","create_autospec vs patch","2026-08-01",[1116,1118,1120,1122],{"q":1011,"a":1117},"They build the same kind of double. The difference is placement: create_autospec returns an object you pass somewhere yourself, while patch(autospec=True) builds one and installs it over a named target for the duration of the block.",{"q":1023,"a":1119},"When the code under test receives an instance rather than a class. create_autospec(Cls) returns a callable class mock whose return_value is the instance double; create_autospec(Cls, instance=True) returns the instance double directly and refuses to be called.",{"q":1038,"a":1121},"Because it introspects the whole object graph at creation time, recursively specced for each attribute. On a class with hundreds of members, or one whose attribute access triggers imports, that cost is paid per call — cache the spec in a fixture rather than rebuilding it per test.",{"q":1044,"a":1123},"Yes. The double copies the real signature, so a call with an unknown keyword or the wrong arity raises TypeError at call time, which a plain Mock accepts silently.",{"name":1125,"description":1126,"steps":1127},"How to choose between create_autospec and patch(autospec=True)","Pick the autospec form that matches how the double reaches the code under test.",[1128,1131,1134,1137],{"name":1129,"text":1130},"Decide how the collaborator is obtained","If the code imports it, patch the name; if the code receives it, construct the double and inject it.",{"name":1132,"text":1133},"Use patch(autospec=True) for imported names","The decorator or context manager replaces the attribute and restores it afterwards.",{"name":1135,"text":1136},"Use create_autospec for injected collaborators","Build the double explicitly, with instance=True when an instance is expected.",{"name":1138,"text":1139},"Verify the contract is enforced","Call the double with a wrong signature and confirm it raises TypeError rather than returning a mock.","\u002Fadvanced-mocking-test-doubles-in-python\u002Fautospec-strict-mocking\u002Fcreate-autospec-vs-patch-autospec-true",{"title":5,"description":1108},"advanced-mocking-test-doubles-in-python\u002Fautospec-strict-mocking\u002Fcreate-autospec-vs-patch-autospec-true\u002Findex","4oRFZx6EG0jPYdYES2yn0bhpdYrX28HMmjVNpzmwucg",1785613403107]