[{"data":1,"prerenderedAt":818},["ShallowReactive",2],{"blog-\u002Fblog\u002Fpython-mocking-patching-explained":3},{"id":4,"title":5,"body":6,"description":804,"difficulty":805,"extension":806,"framework":807,"frameworkSlug":68,"meta":808,"navigation":96,"order":93,"path":809,"qaPath":810,"seo":811,"stem":812,"subtopic":813,"topic":814,"topicSlug":815,"updated":816,"__hash__":817},"blog\u002Fblog\u002Fpython-mocking-patching-explained.md","Python Mocking & Patching Explained — unittest.mock, patch, and Asserting Calls",{"type":7,"value":8,"toc":794},"minimark",[9,14,33,37,63,150,153,157,168,285,290,294,300,463,466,470,493,554,561,565,568,633,636,640,650,736,741,745,790],[10,11,13],"h2",{"id":12},"python-mocking-patching-explained","Python mocking & patching, explained",[15,16,17,18,22,23,27,28,32],"p",{},"Good unit tests isolate the code under test from slow or external things — networks,\ndatabases, the clock. ",[19,20,21],"strong",{},"Mocking"," replaces those dependencies with stand-ins you control and\ninspect. Python's ",[24,25,26],"code",{},"unittest.mock"," does this, and the one rule that trips everyone up is\n",[29,30,31],"em",{},"where"," to patch.",[10,34,36],{"id":35},"mock-objects-record-everything","Mock objects record everything",[15,38,39,40,43,44,47,48,51,52,55,56,55,59,62],{},"A ",[24,41,42],{},"Mock"," is a flexible object that auto-creates attributes and methods on access, returns\nanother Mock when called, and ",[19,45,46],{},"records"," how it was used. ",[24,49,50],{},"MagicMock"," adds support for dunder\nmethods (so it works with ",[24,53,54],{},"len",", ",[24,57,58],{},"[]",[24,60,61],{},"with",", etc.).",[64,65,70],"pre",{"className":66,"code":67,"language":68,"meta":69,"style":69},"language-python shiki shiki-themes github-light github-dark","from unittest.mock import Mock\n\nservice = Mock()\nservice.fetch(42)                 # returns a Mock, records the call\nservice.fetch.assert_called_once_with(42)   # verify it happened\nservice.fetch.call_count          # 1\n","python","",[24,71,72,91,98,110,127,141],{"__ignoreMap":69},[73,74,77,81,85,88],"span",{"class":75,"line":76},"line",1,[73,78,80],{"class":79},"szBVR","from",[73,82,84],{"class":83},"sVt8B"," unittest.mock ",[73,86,87],{"class":79},"import",[73,89,90],{"class":83}," Mock\n",[73,92,94],{"class":75,"line":93},2,[73,95,97],{"emptyLinePlaceholder":96},true,"\n",[73,99,101,104,107],{"class":75,"line":100},3,[73,102,103],{"class":83},"service ",[73,105,106],{"class":79},"=",[73,108,109],{"class":83}," Mock()\n",[73,111,113,116,120,123],{"class":75,"line":112},4,[73,114,115],{"class":83},"service.fetch(",[73,117,119],{"class":118},"sj4cs","42",[73,121,122],{"class":83},")                 ",[73,124,126],{"class":125},"sJ8bj","# returns a Mock, records the call\n",[73,128,130,133,135,138],{"class":75,"line":129},5,[73,131,132],{"class":83},"service.fetch.assert_called_once_with(",[73,134,119],{"class":118},[73,136,137],{"class":83},")   ",[73,139,140],{"class":125},"# verify it happened\n",[73,142,144,147],{"class":75,"line":143},6,[73,145,146],{"class":83},"service.fetch.call_count          ",[73,148,149],{"class":125},"# 1\n",[15,151,152],{},"You don't define anything up front — the Mock fabricates attributes on demand and remembers\nevery interaction for later assertions.",[10,154,156],{"id":155},"setting-return-values-and-side-effects","Setting return values and side effects",[15,158,159,160,163,164,167],{},"Control what a mock returns with ",[24,161,162],{},"return_value",", or use ",[24,165,166],{},"side_effect"," for dynamic behaviour,\nraising exceptions, or returning different values on successive calls.",[64,169,171],{"className":66,"code":170,"language":68,"meta":69,"style":69},"from unittest.mock import Mock\n\nm = Mock()\nm.get.return_value = {\"id\": 1}        # always returns this\nm.get()                               # {'id': 1}\n\nm.fetch.side_effect = ValueError(\"boom\")   # raises when called\nm.next.side_effect = [1, 2, 3]             # returns 1, then 2, then 3\n",[24,172,173,183,187,196,222,230,234,256],{"__ignoreMap":69},[73,174,175,177,179,181],{"class":75,"line":76},[73,176,80],{"class":79},[73,178,84],{"class":83},[73,180,87],{"class":79},[73,182,90],{"class":83},[73,184,185],{"class":75,"line":93},[73,186,97],{"emptyLinePlaceholder":96},[73,188,189,192,194],{"class":75,"line":100},[73,190,191],{"class":83},"m ",[73,193,106],{"class":79},[73,195,109],{"class":83},[73,197,198,201,203,206,210,213,216,219],{"class":75,"line":112},[73,199,200],{"class":83},"m.get.return_value ",[73,202,106],{"class":79},[73,204,205],{"class":83}," {",[73,207,209],{"class":208},"sZZnC","\"id\"",[73,211,212],{"class":83},": ",[73,214,215],{"class":118},"1",[73,217,218],{"class":83},"}        ",[73,220,221],{"class":125},"# always returns this\n",[73,223,224,227],{"class":75,"line":129},[73,225,226],{"class":83},"m.get()                               ",[73,228,229],{"class":125},"# {'id': 1}\n",[73,231,232],{"class":75,"line":143},[73,233,97],{"emptyLinePlaceholder":96},[73,235,237,240,242,245,248,251,253],{"class":75,"line":236},7,[73,238,239],{"class":83},"m.fetch.side_effect ",[73,241,106],{"class":79},[73,243,244],{"class":118}," ValueError",[73,246,247],{"class":83},"(",[73,249,250],{"class":208},"\"boom\"",[73,252,137],{"class":83},[73,254,255],{"class":125},"# raises when called\n",[73,257,259,262,264,267,269,271,274,276,279,282],{"class":75,"line":258},8,[73,260,261],{"class":83},"m.next.side_effect ",[73,263,106],{"class":79},[73,265,266],{"class":83}," [",[73,268,215],{"class":118},[73,270,55],{"class":83},[73,272,273],{"class":118},"2",[73,275,55],{"class":83},[73,277,278],{"class":118},"3",[73,280,281],{"class":83},"]             ",[73,283,284],{"class":125},"# returns 1, then 2, then 3\n",[15,286,287,289],{},[24,288,166],{}," can also be a function, letting the mock compute a result from its arguments.",[10,291,293],{"id":292},"patch-replaces-a-name-temporarily","patch replaces a name temporarily",[15,295,296,299],{},[24,297,298],{},"patch"," swaps a real object for a mock for the duration of a test, then restores it. Use it as\na decorator or context manager.",[64,301,303],{"className":66,"code":302,"language":68,"meta":69,"style":69},"from unittest.mock import patch\n\n# code under test: app.py calls requests.get(...)\ndef get_user(uid):\n    import requests\n    return requests.get(f\"\u002Fusers\u002F{uid}\").json()\n\n@patch(\"app.requests\")               # replace requests inside app\ndef test_get_user(mock_requests):\n    mock_requests.get.return_value.json.return_value = {\"id\": 1}\n    assert get_user(1) == {\"id\": 1}\n    mock_requests.get.assert_called_once_with(\"\u002Fusers\u002F1\")\n",[24,304,305,316,320,325,337,345,374,378,394,405,424,451],{"__ignoreMap":69},[73,306,307,309,311,313],{"class":75,"line":76},[73,308,80],{"class":79},[73,310,84],{"class":83},[73,312,87],{"class":79},[73,314,315],{"class":83}," patch\n",[73,317,318],{"class":75,"line":93},[73,319,97],{"emptyLinePlaceholder":96},[73,321,322],{"class":75,"line":100},[73,323,324],{"class":125},"# code under test: app.py calls requests.get(...)\n",[73,326,327,330,334],{"class":75,"line":112},[73,328,329],{"class":79},"def",[73,331,333],{"class":332},"sScJk"," get_user",[73,335,336],{"class":83},"(uid):\n",[73,338,339,342],{"class":75,"line":129},[73,340,341],{"class":79},"    import",[73,343,344],{"class":83}," requests\n",[73,346,347,350,353,356,359,362,365,368,371],{"class":75,"line":143},[73,348,349],{"class":79},"    return",[73,351,352],{"class":83}," requests.get(",[73,354,355],{"class":79},"f",[73,357,358],{"class":208},"\"\u002Fusers\u002F",[73,360,361],{"class":118},"{",[73,363,364],{"class":83},"uid",[73,366,367],{"class":118},"}",[73,369,370],{"class":208},"\"",[73,372,373],{"class":83},").json()\n",[73,375,376],{"class":75,"line":236},[73,377,97],{"emptyLinePlaceholder":96},[73,379,380,383,385,388,391],{"class":75,"line":258},[73,381,382],{"class":332},"@patch",[73,384,247],{"class":83},[73,386,387],{"class":208},"\"app.requests\"",[73,389,390],{"class":83},")               ",[73,392,393],{"class":125},"# replace requests inside app\n",[73,395,397,399,402],{"class":75,"line":396},9,[73,398,329],{"class":79},[73,400,401],{"class":332}," test_get_user",[73,403,404],{"class":83},"(mock_requests):\n",[73,406,408,411,413,415,417,419,421],{"class":75,"line":407},10,[73,409,410],{"class":83},"    mock_requests.get.return_value.json.return_value ",[73,412,106],{"class":79},[73,414,205],{"class":83},[73,416,209],{"class":208},[73,418,212],{"class":83},[73,420,215],{"class":118},[73,422,423],{"class":83},"}\n",[73,425,427,430,433,435,438,441,443,445,447,449],{"class":75,"line":426},11,[73,428,429],{"class":79},"    assert",[73,431,432],{"class":83}," get_user(",[73,434,215],{"class":118},[73,436,437],{"class":83},") ",[73,439,440],{"class":79},"==",[73,442,205],{"class":83},[73,444,209],{"class":208},[73,446,212],{"class":83},[73,448,215],{"class":118},[73,450,423],{"class":83},[73,452,454,457,460],{"class":75,"line":453},12,[73,455,456],{"class":83},"    mock_requests.get.assert_called_once_with(",[73,458,459],{"class":208},"\"\u002Fusers\u002F1\"",[73,461,462],{"class":83},")\n",[15,464,465],{},"The injected mock is passed as an argument (decorator) so you can configure and assert on it.",[10,467,469],{"id":468},"the-golden-rule-patch-where-its-used","The golden rule: patch where it's used",[15,471,472,473,476,477,480,481,484,485,488,489,492],{},"This is the #1 mocking mistake. You must patch the name ",[19,474,475],{},"in the module where it's looked\nup",", not where it's defined. If ",[24,478,479],{},"app.py"," does ",[24,482,483],{},"from utils import fetch",", patch\n",[24,486,487],{},"app.fetch",", not ",[24,490,491],{},"utils.fetch",".",[64,494,496],{"className":66,"code":495,"language":68,"meta":69,"style":69},"# utils.py:  def fetch(): ...\n# app.py:    from utils import fetch   → creates app.fetch\n\n@patch(\"utils.fetch\")   # WRONG — app already bound its own reference\n@patch(\"app.fetch\")     # RIGHT — patch the name app actually calls\ndef test_it(mock_fetch): ...\n",[24,497,498,503,508,512,526,541],{"__ignoreMap":69},[73,499,500],{"class":75,"line":76},[73,501,502],{"class":125},"# utils.py:  def fetch(): ...\n",[73,504,505],{"class":75,"line":93},[73,506,507],{"class":125},"# app.py:    from utils import fetch   → creates app.fetch\n",[73,509,510],{"class":75,"line":100},[73,511,97],{"emptyLinePlaceholder":96},[73,513,514,516,518,521,523],{"class":75,"line":112},[73,515,382],{"class":332},[73,517,247],{"class":83},[73,519,520],{"class":208},"\"utils.fetch\"",[73,522,137],{"class":83},[73,524,525],{"class":125},"# WRONG — app already bound its own reference\n",[73,527,528,530,532,535,538],{"class":75,"line":129},[73,529,382],{"class":332},[73,531,247],{"class":83},[73,533,534],{"class":208},"\"app.fetch\"",[73,536,537],{"class":83},")     ",[73,539,540],{"class":125},"# RIGHT — patch the name app actually calls\n",[73,542,543,545,548,551],{"class":75,"line":143},[73,544,329],{"class":79},[73,546,547],{"class":332}," test_it",[73,549,550],{"class":83},"(mock_fetch): ",[73,552,553],{"class":118},"...\n",[15,555,556,557,560],{},"Because ",[24,558,559],{},"from x import y"," copies the reference into the importing module, patching the origin\nhas no effect on the already-bound name.",[10,562,564],{"id":563},"asserting-how-a-mock-was-called","Asserting how a mock was called",[15,566,567],{},"Mocks accumulate a rich call history you can assert against — that the call happened, with\nwhat arguments, how many times, and in what order.",[64,569,571],{"className":66,"code":570,"language":68,"meta":69,"style":69},"m.method.assert_called()                  # called at least once\nm.method.assert_called_once()             # exactly once\nm.method.assert_called_with(1, key=\"v\")   # most recent call's args\nm.method.assert_not_called()\nm.method.call_args                        # call(1, key='v')\nm.method.call_args_list                   # every call, in order\n",[24,572,573,581,589,612,617,625],{"__ignoreMap":69},[73,574,575,578],{"class":75,"line":76},[73,576,577],{"class":83},"m.method.assert_called()                  ",[73,579,580],{"class":125},"# called at least once\n",[73,582,583,586],{"class":75,"line":93},[73,584,585],{"class":83},"m.method.assert_called_once()             ",[73,587,588],{"class":125},"# exactly once\n",[73,590,591,594,596,598,602,604,607,609],{"class":75,"line":100},[73,592,593],{"class":83},"m.method.assert_called_with(",[73,595,215],{"class":118},[73,597,55],{"class":83},[73,599,601],{"class":600},"s4XuR","key",[73,603,106],{"class":79},[73,605,606],{"class":208},"\"v\"",[73,608,137],{"class":83},[73,610,611],{"class":125},"# most recent call's args\n",[73,613,614],{"class":75,"line":112},[73,615,616],{"class":83},"m.method.assert_not_called()\n",[73,618,619,622],{"class":75,"line":129},[73,620,621],{"class":83},"m.method.call_args                        ",[73,623,624],{"class":125},"# call(1, key='v')\n",[73,626,627,630],{"class":75,"line":143},[73,628,629],{"class":83},"m.method.call_args_list                   ",[73,631,632],{"class":125},"# every call, in order\n",[15,634,635],{},"These turn \"did my code interact with the dependency correctly?\" into precise checks.",[10,637,639],{"id":638},"patchobject-and-autospec","patch.object and autospec",[15,641,642,645,646,649],{},[24,643,644],{},"patch.object"," patches a single attribute of an object (cleaner than a string path), and\n",[24,647,648],{},"autospec=True"," makes the mock match the real signature so bad calls fail the test instead of\nsilently passing.",[64,651,653],{"className":66,"code":652,"language":68,"meta":69,"style":69},"from unittest.mock import patch\n\nwith patch.object(MyClass, \"method\", return_value=5):\n    ...\n\n@patch(\"app.client\", autospec=True)   # enforces the real interface\ndef test_safe(mock_client): ...\n",[24,654,655,665,669,691,696,700,724],{"__ignoreMap":69},[73,656,657,659,661,663],{"class":75,"line":76},[73,658,80],{"class":79},[73,660,84],{"class":83},[73,662,87],{"class":79},[73,664,315],{"class":83},[73,666,667],{"class":75,"line":93},[73,668,97],{"emptyLinePlaceholder":96},[73,670,671,673,676,679,681,683,685,688],{"class":75,"line":100},[73,672,61],{"class":79},[73,674,675],{"class":83}," patch.object(MyClass, ",[73,677,678],{"class":208},"\"method\"",[73,680,55],{"class":83},[73,682,162],{"class":600},[73,684,106],{"class":79},[73,686,687],{"class":118},"5",[73,689,690],{"class":83},"):\n",[73,692,693],{"class":75,"line":112},[73,694,695],{"class":118},"    ...\n",[73,697,698],{"class":75,"line":129},[73,699,97],{"emptyLinePlaceholder":96},[73,701,702,704,706,709,711,714,716,719,721],{"class":75,"line":143},[73,703,382],{"class":332},[73,705,247],{"class":83},[73,707,708],{"class":208},"\"app.client\"",[73,710,55],{"class":83},[73,712,713],{"class":600},"autospec",[73,715,106],{"class":79},[73,717,718],{"class":118},"True",[73,720,137],{"class":83},[73,722,723],{"class":125},"# enforces the real interface\n",[73,725,726,728,731,734],{"class":75,"line":236},[73,727,329],{"class":79},[73,729,730],{"class":332}," test_safe",[73,732,733],{"class":83},"(mock_client): ",[73,735,553],{"class":118},[15,737,738,740],{},[24,739,713],{}," catches the dangerous case where a mock happily accepts a call that the real object\nwould reject.",[10,742,744],{"id":743},"recap","Recap",[15,746,747,749,750,753,754,757,758,760,761,763,764,768,769,55,772,775,776,779,780,783,784,786,787,789],{},[24,748,26],{}," isolates code by replacing dependencies with ",[19,751,752],{},"Mock\u002FMagicMock"," objects that\nfabricate attributes and ",[19,755,756],{},"record"," every call. Drive behaviour with ",[24,759,162],{}," and\n",[24,762,166],{}," (values, exceptions, or a function), swap real names with ",[19,765,766],{},[24,767,298],{}," (decorator\nor context manager), and verify interactions with ",[24,770,771],{},"assert_called_*",[24,773,774],{},"call_args",", and\n",[24,777,778],{},"call_args_list",". The crucial rule: ",[19,781,782],{},"patch where the name is used",", not where it's defined.\nUse ",[24,785,644],{}," for a single attribute and ",[24,788,648],{}," to keep mocks honest about\nsignatures.",[791,792,793],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":69,"searchDepth":93,"depth":93,"links":795},[796,797,798,799,800,801,802,803],{"id":12,"depth":93,"text":13},{"id":35,"depth":93,"text":36},{"id":155,"depth":93,"text":156},{"id":292,"depth":93,"text":293},{"id":468,"depth":93,"text":469},{"id":563,"depth":93,"text":564},{"id":638,"depth":93,"text":639},{"id":743,"depth":93,"text":744},"How to isolate code under test with unittest.mock — Mock and MagicMock, patching with the right target, setting return values and side effects, asserting calls, and the \"patch where it's used\" rule.","medium","md","Python",{},"\u002Fblog\u002Fpython-mocking-patching-explained","\u002Fpython\u002Ftesting\u002Fmocking",{"title":5,"description":804},"blog\u002Fpython-mocking-patching-explained","Mocking & Patching","Testing","testing","2026-06-19","BFri5PglARDGnbVve__oH0mFa6iScPfr5QmNuv4zzZg",1782244092959]