1:"$Sreact.fragment"
2:I[15579,["/_next/static/chunks/fd0661f1506dcbc6.js","/_next/static/chunks/94245cbda44972fe.js","/_next/static/chunks/af778fff4a0f4be6.js","/_next/static/chunks/b096b037d08e2f31.js"],"Navigation"]
3:I[3013,["/_next/static/chunks/fd0661f1506dcbc6.js","/_next/static/chunks/94245cbda44972fe.js","/_next/static/chunks/af778fff4a0f4be6.js","/_next/static/chunks/b096b037d08e2f31.js"],""]
17:I[41451,["/_next/static/chunks/fd0661f1506dcbc6.js","/_next/static/chunks/94245cbda44972fe.js","/_next/static/chunks/af778fff4a0f4be6.js","/_next/static/chunks/b096b037d08e2f31.js"],"Footer"]
18:I[47913,["/_next/static/chunks/316a3a63422f35de.js"],"OutletBoundary"]
19:"$Sreact.suspense"
:HL["/blog/posts/monkeypatch-boundary/hero.jpg","image"]
:HL["/blog/posts/logo.png","image"]
0:{"buildId":"TlpKRvbES4zzM7LeczAM7","rsc":["$","$1","c",{"children":[[["$","$L2",null,{}],["$","main",null,{"className":"pt-20 md:pt-24","children":["$","article",null,{"children":[["$","header",null,{"className":"border-b border-border","children":["$","div",null,{"className":"container mx-auto px-6 py-14 md:py-20","children":[["$","$L3",null,{"href":"/blog","className":"mb-8 inline-flex items-center text-sm font-medium text-muted-foreground transition-colors hover:text-foreground","children":[["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-left mr-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1l729n",{"d":"m12 19-7-7 7-7"}],["$","path","x3x0zl",{"d":"M19 12H5"}],"$undefined"]}],"Blog"]}],["$","div",null,{"className":"grid gap-8 md:grid-cols-[1fr_0.9fr] md:items-start","children":[["$","div",null,{"className":"max-w-3xl","children":[["$","div",null,{"className":"mb-5 flex flex-wrap items-center gap-3 text-sm text-muted-foreground","children":[["$","span",null,{"className":"rounded-md border border-primary/40 bg-primary/10 px-2.5 py-1 text-primary","children":"Testing"}],["$","span",null,{"children":"2026-W18"}],["$","span",null,{"aria-hidden":"true","children":"/"}],["$","span",null,{"children":"3 min read"}],null]}],["$","h1",null,{"className":"text-4xl font-bold leading-tight text-balance md:text-6xl","children":"Monkeypatching at the Right Boundary"}],["$","p",null,{"className":"mt-6 text-lg leading-relaxed text-muted-foreground md:text-xl","children":"Your monkeypatch looks correct, the function name resolves, the test still sees the production value. The cause is almost always import-time binding — patch the symbol where the caller resolves it, not where it's defined."}]]}],["$","div",null,{"className":"overflow-hidden rounded-lg border border-border bg-card","children":["$","div",null,{"className":"relative aspect-[16/9] overflow-hidden","children":[null,["$","img",null,{"src":"/blog/posts/monkeypatch-boundary/hero.jpg","alt":"A single brass valve mid-flow on a polished copper pipe assembly against a charcoal background, sharp side lighting catching the valve's machined edges, no people, editorial.","className":"h-full w-full object-cover"}],["$","img",null,{"src":"/blog/posts/logo.png","alt":"","aria-hidden":"true","className":"pointer-events-none absolute right-4 top-4 h-[50px] w-[50px] mix-blend-screen"}]]}]}]]}]]}]}],["$","div",null,{"className":"container mx-auto px-6 py-12 md:py-16","children":["$","div",null,{"className":"grid gap-10 lg:grid-cols-[minmax(0,1fr)_280px] lg:items-start","children":[["$","div",null,{"className":"max-w-3xl text-muted-foreground","children":[[["$","h2","h2-0",{"className":"mt-12 text-2xl font-semibold leading-snug text-foreground first:mt-0","children":"The problem"}],"\n",["$","p","p-0",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":"A pytest test patches a helper function to control its return value, but the test keeps failing because the patched value never appears. The function being patched exists in the module, the monkeypatch call looks correct — and yet the production value keeps showing up."}],"\n",["$","h2","h2-1",{"className":"mt-12 text-2xl font-semibold leading-snug text-foreground first:mt-0","children":"The approach"}],"\n",["$","p","p-1",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["The root cause is almost always that the ",["$","em","em-0",{"children":"caller"}]," has already imported the symbol before the patch runs, so the patch changes the name in the target module but the caller's local reference is unaffected. The fix is to patch at the ","$L4",", not at the definition."]}],"\n","$L5","\n","$L6","\n","$L7","\n","$L8","\n","$L9"],"$La",null]}],null]}]}]]}]}],"$Lb"],["$Lc","$Ld","$Le"],"$Lf"]}],"loading":null,"isPartial":false}
4:["$","em","em-1",{"children":"caller's import boundary"}]
5:["$","p","p-2",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["If ",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"module_a.py"}]," does ",["$","code","code-1",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"from module_b import helper_fn"}]," and you monkeypatch ",["$","code","code-2",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"module_b.helper_fn"}],", ",["$","code","code-3",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"module_a"}]," still holds its own reference to the original. You need to patch ",["$","code","code-4",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"module_a.helper_fn"}]," instead."]}]
6:["$","p","p-3",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["A subtler version: ",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"helper_fn"}]," calls a sub-function internally. You patch the sub-function expecting ",["$","code","code-1",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"helper_fn"}]," to return a different value — but the real ",["$","code","code-2",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"helper_fn"}]," is installed in the test environment and runs normally, ignoring your patch of its internal dependency. The fix is to patch ",["$","code","code-3",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"helper_fn"}]," itself."]}]
7:["$","div","pre-0",{"style":{"background":"hsl(220, 13%, 18%)","color":"hsl(220, 14%, 71%)","textShadow":"0 1px rgba(0, 0, 0, 0.3)","fontFamily":"\"Fira Code\", \"Fira Mono\", Menlo, Consolas, \"DejaVu Sans Mono\", monospace","direction":"ltr","textAlign":"left","whiteSpace":"pre","wordSpacing":"normal","wordBreak":"normal","lineHeight":"1.5","MozTabSize":"2","OTabSize":"2","tabSize":"2","WebkitHyphens":"none","MozHyphens":"none","msHyphens":"none","hyphens":"none","padding":"1rem","margin":"0.5em 0","overflow":"auto","borderRadius":"0.5rem","marginTop":"1.25rem","marginBottom":0,"border":"1px solid hsl(var(--border))","fontSize":"0.875rem"},"children":["$","code",null,{"style":{"whiteSpace":"pre","fontFamily":"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace"},"children":[false,[["$","span","code-segment-0",{"className":"token","style":{"color":"hsl(220, 10%, 40%)","fontStyle":"italic"},"children":["# Wrong — patches the implementation detail"]}],["$","span","code-segment-1",{"style":{},"children":["\n"]}],["$","span","code-segment-2",{"style":{},"children":["monkeypatch"]}],["$","span","code-segment-3",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["."]}],["$","span","code-segment-4",{"className":"token","style":{"color":"hsl(95, 38%, 62%)"},"children":["setattr"]}],["$","span","code-segment-5",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["("]}],["$","span","code-segment-6",{"style":{},"children":["module_a"]}],["$","span","code-segment-7",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[","]}],["$","span","code-segment-8",{"style":{},"children":[" "]}],["$","span","code-segment-9",{"className":"token","style":{"color":"hsl(95, 38%, 62%)"},"children":["\"_char_fallback_estimate\""]}],["$","span","code-segment-10",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[","]}],["$","span","code-segment-11",{"style":{},"children":[" "]}],["$","span","code-segment-12",{"className":"token","style":{"color":"hsl(286, 60%, 67%)"},"children":["lambda"]}],["$","span","code-segment-13",{"style":{},"children":[" "]}],["$","span","code-segment-14",{"className":"token","style":{"color":"hsl(207, 82%, 66%)"},"children":["*"]}],["$","span","code-segment-15",{"style":{},"children":["a"]}],["$","span","code-segment-16",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[":"]}],["$","span","code-segment-17",{"style":{},"children":[" "]}],["$","span","code-segment-18",{"className":"token","style":{"color":"hsl(29, 54%, 61%)"},"children":["500"]}],["$","span","code-segment-19",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[")"]}],["$","span","code-segment-20",{"style":{},"children":["\n"]}],"\n",["$","span","code-segment-22",{"style":{},"children":[""]}],["$","span","code-segment-23",{"className":"token","style":{"color":"hsl(220, 10%, 40%)","fontStyle":"italic"},"children":["# Right — patches the function the caller actually calls"]}],["$","span","code-segment-24",{"style":{},"children":["\n"]}],["$","span","code-segment-25",{"style":{},"children":["monkeypatch"]}],["$","span","code-segment-26",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["."]}],["$","span","code-segment-27",{"className":"token","style":{"color":"hsl(95, 38%, 62%)"},"children":["setattr"]}],["$","span","code-segment-28",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["("]}],["$","span","code-segment-29",{"style":{},"children":["module_a"]}],["$","span","code-segment-30",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[","]}],["$","span","code-segment-31",{"style":{},"children":[" "]}],["$","span","code-segment-32",{"className":"token","style":{"color":"hsl(95, 38%, 62%)"},"children":["\"estimate_input_tokens\""]}],["$","span","code-segment-33",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[","]}],["$","span","code-segment-34",{"style":{},"children":[" "]}],["$","span","code-segment-35",{"className":"token","style":{"color":"hsl(286, 60%, 67%)"},"children":["lambda"]}],["$","span","code-segment-36",{"style":{},"children":[" "]}],["$","span","code-segment-37",{"className":"token","style":{"color":"hsl(207, 82%, 66%)"},"children":["*"]}],["$","span","code-segment-38",{"style":{},"children":["a"]}],["$","span","code-segment-39",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[","]}],"$L10","$L11","$L12","$L13","$L14","$L15","$L16"]]}]}]
8:["$","h2","h2-2",{"className":"mt-12 text-2xl font-semibold leading-snug text-foreground first:mt-0","children":"What I learned"}]
9:["$","p","p-4",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["The rule is: patch the thing the code under test ",["$","em","em-0",{"children":"calls"}],", at the module where it ",["$","em","em-1",{"children":"resolves the name"}],". When a real package is installed in the test environment (not mocked at the import level), you cannot rely on internal code paths being exercised the same way as in CI without the package. In that situation, the only reliable patch point is the public API boundary — the function your code calls, not the function that function calls."]}]
a:["$","div",null,{"className":"mt-14 border-t border-border pt-8","children":["$","$L3",null,{"href":"/platform","className":"inline-flex items-center text-sm font-medium text-primary transition-colors hover:text-primary/80","children":["Start a build",["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-right ml-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1ays0h",{"d":"M5 12h14"}],["$","path","xquz4c",{"d":"m12 5 7 7-7 7"}],"$undefined"]}]]}]}]
b:["$","$L17",null,{}]
c:["$","script","script-0",{"src":"/_next/static/chunks/94245cbda44972fe.js","async":true}]
d:["$","script","script-1",{"src":"/_next/static/chunks/af778fff4a0f4be6.js","async":true}]
e:["$","script","script-2",{"src":"/_next/static/chunks/b096b037d08e2f31.js","async":true}]
f:["$","$L18",null,{"children":["$","$19",null,{"name":"Next.MetadataOutlet","children":"$@1a"}]}]
10:["$","span","code-segment-40",{"style":{},"children":[" "]}]
11:["$","span","code-segment-41",{"className":"token","style":{"color":"hsl(207, 82%, 66%)"},"children":["**"]}]
12:["$","span","code-segment-42",{"style":{},"children":["kw"]}]
13:["$","span","code-segment-43",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[":"]}]
14:["$","span","code-segment-44",{"style":{},"children":[" "]}]
15:["$","span","code-segment-45",{"className":"token","style":{"color":"hsl(29, 54%, 61%)"},"children":["500"]}]
16:["$","span","code-segment-46",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[")"]}]
1a:null
