[{"data":1,"prerenderedAt":3100},["ShallowReactive",2],{"page-\u002Fgo\u002F25-testing-and-benchmarking":3},{"id":4,"title":5,"body":6,"description":3093,"extension":3094,"meta":3095,"navigation":49,"path":3096,"seo":3097,"stem":3098,"__hash__":3099},"content\u002Fgo\u002F25-testing-and-benchmarking.md","25 — Testing & Benchmarking",{"type":7,"value":8,"toc":3088},"minimark",[9,13,18,2908,3071,3075,3084],[10,11,5],"h1",{"id":12},"_25-testing-benchmarking",[14,15,17],"h2",{"id":16},"table-driven-tests-the-production-pattern","Table-Driven Tests — The Production Pattern",[19,20,22,653,657],"code-wrapper",{"language":21},"go",[23,24,28],"pre",{"className":25,"code":26,"language":21,"meta":27,"style":27},"language-go shiki shiki-themes github-light github-dark","package user\n\nimport \"testing\"\n\nfunc TestValidate(t *testing.T) {\n    tests := []struct {\n        name    string\n        input   User\n        wantErr string  \u002F\u002F empty = no error expected\n    }{\n        {\"valid user\", User{Email: \"a@b.com\", Age: 25}, \"\"},\n        {\"empty email\", User{Age: 25}, \"email required\"},\n        {\"invalid email\", User{Email: \"no-at-sign\", Age: 25}, \"email invalid\"},\n        {\"negative age\", User{Email: \"a@b.com\", Age: -1}, \"age must be non-negative\"},\n        {\"zero age (valid)\", User{Email: \"a@b.com\", Age: 0}, \"\"},  \u002F\u002F 0 is valid\n    }\n\n    for _, tt := range tests {\n        t.Run(tt.name, func(t *testing.T) {\n            t.Parallel()  \u002F\u002F ✅ parallel subtests — faster\n\n            err := Validate(tt.input)\n            if tt.wantErr == \"\" {\n                if err != nil {\n                    t.Errorf(\"Validate(%+v) = %v, want nil\", tt.input, err)\n                }\n            } else {\n                if err == nil {\n                    t.Errorf(\"Validate(%+v) = nil, want error containing %q\", tt.input, tt.wantErr)\n                } else if !strings.Contains(err.Error(), tt.wantErr) {\n                    t.Errorf(\"Validate(%+v) = %q, want error containing %q\", tt.input, err.Error(), tt.wantErr)\n                }\n            }\n        })\n    }\n}\n\n\u002F\u002F ┌──────────────────────────────────────────────────────────────────────┐\n\u002F\u002F │ Table-driven test rules:                                            │\n\u002F\u002F │   1. Each case has a name (shown in `go test -v` output)             │\n\u002F\u002F │   2. Use t.Run for subtests — enables t.Parallel and filtering       │\n\u002F\u002F │   3. Include edge cases (zero, negative, empty, boundary values)     │\n\u002F\u002F │   4. Test the error MESSAGE, not just \"error != nil\"                 │\n\u002F\u002F └──────────────────────────────────────────────────────────────────────┘\n","",[29,30,31,44,51,67,72,103,121,130,139,152,158,195,219,247,278,309,315,320,337,365,380,385,399,416,433,462,468,479,492,517,546,578,583,589,595,600,606,611,617,623,629,635,641,647],"code",{"__ignoreMap":27},[32,33,36,40],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"svdQ7","package",[32,41,43],{"class":42},"sIsaT"," user\n",[32,45,47],{"class":34,"line":46},2,[32,48,50],{"emptyLinePlaceholder":49},true,"\n",[32,52,54,57,61,64],{"class":34,"line":53},3,[32,55,56],{"class":38},"import",[32,58,60],{"class":59},"sJ6F3"," \"",[32,62,63],{"class":42},"testing",[32,65,66],{"class":59},"\"\n",[32,68,70],{"class":34,"line":69},4,[32,71,50],{"emptyLinePlaceholder":49},[32,73,75,78,81,85,89,92,94,97,100],{"class":34,"line":74},5,[32,76,77],{"class":38},"func",[32,79,80],{"class":42}," TestValidate",[32,82,84],{"class":83},"ssxIu","(",[32,86,88],{"class":87},"sCrzJ","t",[32,90,91],{"class":38}," *",[32,93,63],{"class":42},[32,95,96],{"class":83},".",[32,98,99],{"class":42},"T",[32,101,102],{"class":83},") {\n",[32,104,106,109,112,115,118],{"class":34,"line":105},6,[32,107,108],{"class":83},"    tests ",[32,110,111],{"class":38},":=",[32,113,114],{"class":83}," []",[32,116,117],{"class":38},"struct",[32,119,120],{"class":83}," {\n",[32,122,124,127],{"class":34,"line":123},7,[32,125,126],{"class":83},"        name    ",[32,128,129],{"class":38},"string\n",[32,131,133,136],{"class":34,"line":132},8,[32,134,135],{"class":83},"        input   ",[32,137,138],{"class":42},"User\n",[32,140,142,145,148],{"class":34,"line":141},9,[32,143,144],{"class":83},"        wantErr ",[32,146,147],{"class":38},"string",[32,149,151],{"class":150},"sdCPZ","  \u002F\u002F empty = no error expected\n",[32,153,155],{"class":34,"line":154},10,[32,156,157],{"class":83},"    }{\n",[32,159,161,164,167,170,173,176,179,182,186,189,192],{"class":34,"line":160},11,[32,162,163],{"class":83},"        {",[32,165,166],{"class":59},"\"valid user\"",[32,168,169],{"class":83},", ",[32,171,172],{"class":42},"User",[32,174,175],{"class":83},"{Email: ",[32,177,178],{"class":59},"\"a@b.com\"",[32,180,181],{"class":83},", Age: ",[32,183,185],{"class":184},"snvgF","25",[32,187,188],{"class":83},"}, ",[32,190,191],{"class":59},"\"\"",[32,193,194],{"class":83},"},\n",[32,196,198,200,203,205,207,210,212,214,217],{"class":34,"line":197},12,[32,199,163],{"class":83},[32,201,202],{"class":59},"\"empty email\"",[32,204,169],{"class":83},[32,206,172],{"class":42},[32,208,209],{"class":83},"{Age: ",[32,211,185],{"class":184},[32,213,188],{"class":83},[32,215,216],{"class":59},"\"email required\"",[32,218,194],{"class":83},[32,220,222,224,227,229,231,233,236,238,240,242,245],{"class":34,"line":221},13,[32,223,163],{"class":83},[32,225,226],{"class":59},"\"invalid email\"",[32,228,169],{"class":83},[32,230,172],{"class":42},[32,232,175],{"class":83},[32,234,235],{"class":59},"\"no-at-sign\"",[32,237,181],{"class":83},[32,239,185],{"class":184},[32,241,188],{"class":83},[32,243,244],{"class":59},"\"email invalid\"",[32,246,194],{"class":83},[32,248,250,252,255,257,259,261,263,265,268,271,273,276],{"class":34,"line":249},14,[32,251,163],{"class":83},[32,253,254],{"class":59},"\"negative age\"",[32,256,169],{"class":83},[32,258,172],{"class":42},[32,260,175],{"class":83},[32,262,178],{"class":59},[32,264,181],{"class":83},[32,266,267],{"class":38},"-",[32,269,270],{"class":184},"1",[32,272,188],{"class":83},[32,274,275],{"class":59},"\"age must be non-negative\"",[32,277,194],{"class":83},[32,279,281,283,286,288,290,292,294,296,299,301,303,306],{"class":34,"line":280},15,[32,282,163],{"class":83},[32,284,285],{"class":59},"\"zero age (valid)\"",[32,287,169],{"class":83},[32,289,172],{"class":42},[32,291,175],{"class":83},[32,293,178],{"class":59},[32,295,181],{"class":83},[32,297,298],{"class":184},"0",[32,300,188],{"class":83},[32,302,191],{"class":59},[32,304,305],{"class":83},"},  ",[32,307,308],{"class":150},"\u002F\u002F 0 is valid\n",[32,310,312],{"class":34,"line":311},16,[32,313,314],{"class":83},"    }\n",[32,316,318],{"class":34,"line":317},17,[32,319,50],{"emptyLinePlaceholder":49},[32,321,323,326,329,331,334],{"class":34,"line":322},18,[32,324,325],{"class":38},"    for",[32,327,328],{"class":83}," _, tt ",[32,330,111],{"class":38},[32,332,333],{"class":38}," range",[32,335,336],{"class":83}," tests {\n",[32,338,340,343,346,349,351,353,355,357,359,361,363],{"class":34,"line":339},19,[32,341,342],{"class":83},"        t.",[32,344,345],{"class":42},"Run",[32,347,348],{"class":83},"(tt.name, ",[32,350,77],{"class":38},[32,352,84],{"class":83},[32,354,88],{"class":87},[32,356,91],{"class":38},[32,358,63],{"class":42},[32,360,96],{"class":83},[32,362,99],{"class":42},[32,364,102],{"class":83},[32,366,368,371,374,377],{"class":34,"line":367},20,[32,369,370],{"class":83},"            t.",[32,372,373],{"class":42},"Parallel",[32,375,376],{"class":83},"()  ",[32,378,379],{"class":150},"\u002F\u002F ✅ parallel subtests — faster\n",[32,381,383],{"class":34,"line":382},21,[32,384,50],{"emptyLinePlaceholder":49},[32,386,388,391,393,396],{"class":34,"line":387},22,[32,389,390],{"class":83},"            err ",[32,392,111],{"class":38},[32,394,395],{"class":42}," Validate",[32,397,398],{"class":83},"(tt.input)\n",[32,400,402,405,408,411,414],{"class":34,"line":401},23,[32,403,404],{"class":38},"            if",[32,406,407],{"class":83}," tt.wantErr ",[32,409,410],{"class":38},"==",[32,412,413],{"class":59}," \"\"",[32,415,120],{"class":83},[32,417,419,422,425,428,431],{"class":34,"line":418},24,[32,420,421],{"class":38},"                if",[32,423,424],{"class":83}," err ",[32,426,427],{"class":38},"!=",[32,429,430],{"class":184}," nil",[32,432,120],{"class":83},[32,434,436,439,442,444,447,450,453,456,459],{"class":34,"line":435},25,[32,437,438],{"class":83},"                    t.",[32,440,441],{"class":42},"Errorf",[32,443,84],{"class":83},[32,445,446],{"class":59},"\"Validate(",[32,448,449],{"class":184},"%+v",[32,451,452],{"class":59},") = ",[32,454,455],{"class":184},"%v",[32,457,458],{"class":59},", want nil\"",[32,460,461],{"class":83},", tt.input, err)\n",[32,463,465],{"class":34,"line":464},26,[32,466,467],{"class":83},"                }\n",[32,469,471,474,477],{"class":34,"line":470},27,[32,472,473],{"class":83},"            } ",[32,475,476],{"class":38},"else",[32,478,120],{"class":83},[32,480,482,484,486,488,490],{"class":34,"line":481},28,[32,483,421],{"class":38},[32,485,424],{"class":83},[32,487,410],{"class":38},[32,489,430],{"class":184},[32,491,120],{"class":83},[32,493,495,497,499,501,503,505,508,511,514],{"class":34,"line":494},29,[32,496,438],{"class":83},[32,498,441],{"class":42},[32,500,84],{"class":83},[32,502,446],{"class":59},[32,504,449],{"class":184},[32,506,507],{"class":59},") = nil, want error containing ",[32,509,510],{"class":184},"%q",[32,512,513],{"class":59},"\"",[32,515,516],{"class":83},", tt.input, tt.wantErr)\n",[32,518,520,523,525,528,531,534,537,540,543],{"class":34,"line":519},30,[32,521,522],{"class":83},"                } ",[32,524,476],{"class":38},[32,526,527],{"class":38}," if",[32,529,530],{"class":38}," !",[32,532,533],{"class":83},"strings.",[32,535,536],{"class":42},"Contains",[32,538,539],{"class":83},"(err.",[32,541,542],{"class":42},"Error",[32,544,545],{"class":83},"(), tt.wantErr) {\n",[32,547,549,551,553,555,557,559,561,563,566,568,570,573,575],{"class":34,"line":548},31,[32,550,438],{"class":83},[32,552,441],{"class":42},[32,554,84],{"class":83},[32,556,446],{"class":59},[32,558,449],{"class":184},[32,560,452],{"class":59},[32,562,510],{"class":184},[32,564,565],{"class":59},", want error containing ",[32,567,510],{"class":184},[32,569,513],{"class":59},[32,571,572],{"class":83},", tt.input, err.",[32,574,542],{"class":42},[32,576,577],{"class":83},"(), tt.wantErr)\n",[32,579,581],{"class":34,"line":580},32,[32,582,467],{"class":83},[32,584,586],{"class":34,"line":585},33,[32,587,588],{"class":83},"            }\n",[32,590,592],{"class":34,"line":591},34,[32,593,594],{"class":83},"        })\n",[32,596,598],{"class":34,"line":597},35,[32,599,314],{"class":83},[32,601,603],{"class":34,"line":602},36,[32,604,605],{"class":83},"}\n",[32,607,609],{"class":34,"line":608},37,[32,610,50],{"emptyLinePlaceholder":49},[32,612,614],{"class":34,"line":613},38,[32,615,616],{"class":150},"\u002F\u002F ┌──────────────────────────────────────────────────────────────────────┐\n",[32,618,620],{"class":34,"line":619},39,[32,621,622],{"class":150},"\u002F\u002F │ Table-driven test rules:                                            │\n",[32,624,626],{"class":34,"line":625},40,[32,627,628],{"class":150},"\u002F\u002F │   1. Each case has a name (shown in `go test -v` output)             │\n",[32,630,632],{"class":34,"line":631},41,[32,633,634],{"class":150},"\u002F\u002F │   2. Use t.Run for subtests — enables t.Parallel and filtering       │\n",[32,636,638],{"class":34,"line":637},42,[32,639,640],{"class":150},"\u002F\u002F │   3. Include edge cases (zero, negative, empty, boundary values)     │\n",[32,642,644],{"class":34,"line":643},43,[32,645,646],{"class":150},"\u002F\u002F │   4. Test the error MESSAGE, not just \"error != nil\"                 │\n",[32,648,650],{"class":34,"line":649},44,[32,651,652],{"class":150},"\u002F\u002F └──────────────────────────────────────────────────────────────────────┘\n",[14,654,656],{"id":655},"benchmarking-allocation-analysis","Benchmarking — Allocation Analysis",[19,658,659,898,906],{"language":21},[23,660,662],{"className":25,"code":661,"language":21,"meta":27,"style":27},"package user\n\nimport \"testing\"\n\nfunc BenchmarkValidate(b *testing.B) {\n    \u002F\u002F b.N is adjusted by the framework until the run takes ~1 second.\n    \u002F\u002F The loop body must be the operation under test — no setup inside.\n\n    user := User{Email: \"test@example.com\", Age: 30}\n\n    b.ReportAllocs()  \u002F\u002F ✅ show allocs\u002Fop — the key metric\n    b.ResetTimer()    \u002F\u002F ✅ exclude setup from the benchmark\n\n    for i := 0; i \u003C b.N; i++ {\n        _ = Validate(user)  \u002F\u002F discard result — don't let the compiler optimize it away\n    }\n}\n\n\u002F\u002F Run:\n\u002F\u002F   go test -bench=BenchmarkValidate -benchmem -count=5\n\u002F\u002F\n\u002F\u002F Output:\n\u002F\u002F   BenchmarkValidate-8   10000000   112 ns\u002Fop   0 B\u002Fop   0 allocs\u002Fop\n\u002F\u002F\n\u002F\u002F Key metrics:\n\u002F\u002F   ns\u002Fop    — nanoseconds per operation (lower = faster)\n\u002F\u002F   B\u002Fop     — bytes allocated per operation (lower = less GC pressure)\n\u002F\u002F   allocs\u002Fop — heap allocations per operation (lower = less GC pressure)\n\u002F\u002F\n\u002F\u002F ⚠️ allocs\u002Fop is the most important — heap allocations trigger GC.\n\u002F\u002F   Reducing allocs often improves performance more than micro-optimizing.\n",[29,663,664,670,674,684,688,711,716,721,725,747,751,764,777,781,807,823,827,831,835,840,845,850,855,860,864,869,874,879,884,888,893],{"__ignoreMap":27},[32,665,666,668],{"class":34,"line":35},[32,667,39],{"class":38},[32,669,43],{"class":42},[32,671,672],{"class":34,"line":46},[32,673,50],{"emptyLinePlaceholder":49},[32,675,676,678,680,682],{"class":34,"line":53},[32,677,56],{"class":38},[32,679,60],{"class":59},[32,681,63],{"class":42},[32,683,66],{"class":59},[32,685,686],{"class":34,"line":69},[32,687,50],{"emptyLinePlaceholder":49},[32,689,690,692,695,697,700,702,704,706,709],{"class":34,"line":74},[32,691,77],{"class":38},[32,693,694],{"class":42}," BenchmarkValidate",[32,696,84],{"class":83},[32,698,699],{"class":87},"b",[32,701,91],{"class":38},[32,703,63],{"class":42},[32,705,96],{"class":83},[32,707,708],{"class":42},"B",[32,710,102],{"class":83},[32,712,713],{"class":34,"line":105},[32,714,715],{"class":150},"    \u002F\u002F b.N is adjusted by the framework until the run takes ~1 second.\n",[32,717,718],{"class":34,"line":123},[32,719,720],{"class":150},"    \u002F\u002F The loop body must be the operation under test — no setup inside.\n",[32,722,723],{"class":34,"line":132},[32,724,50],{"emptyLinePlaceholder":49},[32,726,727,730,732,735,737,740,742,745],{"class":34,"line":141},[32,728,729],{"class":83},"    user ",[32,731,111],{"class":38},[32,733,734],{"class":42}," User",[32,736,175],{"class":83},[32,738,739],{"class":59},"\"test@example.com\"",[32,741,181],{"class":83},[32,743,744],{"class":184},"30",[32,746,605],{"class":83},[32,748,749],{"class":34,"line":154},[32,750,50],{"emptyLinePlaceholder":49},[32,752,753,756,759,761],{"class":34,"line":160},[32,754,755],{"class":83},"    b.",[32,757,758],{"class":42},"ReportAllocs",[32,760,376],{"class":83},[32,762,763],{"class":150},"\u002F\u002F ✅ show allocs\u002Fop — the key metric\n",[32,765,766,768,771,774],{"class":34,"line":197},[32,767,755],{"class":83},[32,769,770],{"class":42},"ResetTimer",[32,772,773],{"class":83},"()    ",[32,775,776],{"class":150},"\u002F\u002F ✅ exclude setup from the benchmark\n",[32,778,779],{"class":34,"line":221},[32,780,50],{"emptyLinePlaceholder":49},[32,782,783,785,788,790,793,796,799,802,805],{"class":34,"line":249},[32,784,325],{"class":38},[32,786,787],{"class":83}," i ",[32,789,111],{"class":38},[32,791,792],{"class":184}," 0",[32,794,795],{"class":83},"; i ",[32,797,798],{"class":38},"\u003C",[32,800,801],{"class":83}," b.N; i",[32,803,804],{"class":38},"++",[32,806,120],{"class":83},[32,808,809,812,815,817,820],{"class":34,"line":280},[32,810,811],{"class":83},"        _ ",[32,813,814],{"class":38},"=",[32,816,395],{"class":42},[32,818,819],{"class":83},"(user)  ",[32,821,822],{"class":150},"\u002F\u002F discard result — don't let the compiler optimize it away\n",[32,824,825],{"class":34,"line":311},[32,826,314],{"class":83},[32,828,829],{"class":34,"line":317},[32,830,605],{"class":83},[32,832,833],{"class":34,"line":322},[32,834,50],{"emptyLinePlaceholder":49},[32,836,837],{"class":34,"line":339},[32,838,839],{"class":150},"\u002F\u002F Run:\n",[32,841,842],{"class":34,"line":367},[32,843,844],{"class":150},"\u002F\u002F   go test -bench=BenchmarkValidate -benchmem -count=5\n",[32,846,847],{"class":34,"line":382},[32,848,849],{"class":150},"\u002F\u002F\n",[32,851,852],{"class":34,"line":387},[32,853,854],{"class":150},"\u002F\u002F Output:\n",[32,856,857],{"class":34,"line":401},[32,858,859],{"class":150},"\u002F\u002F   BenchmarkValidate-8   10000000   112 ns\u002Fop   0 B\u002Fop   0 allocs\u002Fop\n",[32,861,862],{"class":34,"line":418},[32,863,849],{"class":150},[32,865,866],{"class":34,"line":435},[32,867,868],{"class":150},"\u002F\u002F Key metrics:\n",[32,870,871],{"class":34,"line":464},[32,872,873],{"class":150},"\u002F\u002F   ns\u002Fop    — nanoseconds per operation (lower = faster)\n",[32,875,876],{"class":34,"line":470},[32,877,878],{"class":150},"\u002F\u002F   B\u002Fop     — bytes allocated per operation (lower = less GC pressure)\n",[32,880,881],{"class":34,"line":481},[32,882,883],{"class":150},"\u002F\u002F   allocs\u002Fop — heap allocations per operation (lower = less GC pressure)\n",[32,885,886],{"class":34,"line":494},[32,887,849],{"class":150},[32,889,890],{"class":34,"line":519},[32,891,892],{"class":150},"\u002F\u002F ⚠️ allocs\u002Fop is the most important — heap allocations trigger GC.\n",[32,894,895],{"class":34,"line":548},[32,896,897],{"class":150},"\u002F\u002F   Reducing allocs often improves performance more than micro-optimizing.\n",[899,900,902,903],"h3",{"id":901},"benchmark-comparison-benchstat","Benchmark Comparison — ",[29,904,905],{},"benchstat",[19,907,909,1049,1053],{"language":908},"bash",[23,910,913],{"className":911,"code":912,"language":908,"meta":27,"style":27},"language-bash shiki shiki-themes github-light github-dark","# Compare two implementations statistically (eliminates noise):\n# 1. Benchmark the old version:\ngit stash\ngo test -bench=BenchmarkValidate -benchmem -count=10 > old.txt\n\n# 2. Benchmark the new version:\ngit stash pop\ngo test -bench=BenchmarkValidate -benchmem -count=10 > new.txt\n\n# 3. Compare:\nbenchstat old.txt new.txt\n# goos: darwin\n# goarch: arm64\n#                      │   old.txt   │               new.txt               │\n#                      │   sec\u002Fop    │  sec\u002Fop     vs base                │\n# Validate-8             112.2n ± 1%   85.4n ± 2%  -23.89% (p=0.000 n=10)\n#\n# The p-value \u003C 0.05 means the difference is statistically significant.\n# \"vs base -23.89%\" means the new version is 23.89% faster.\n",[29,914,915,920,925,933,955,959,964,974,991,995,1000,1009,1014,1019,1024,1029,1034,1039,1044],{"__ignoreMap":27},[32,916,917],{"class":34,"line":35},[32,918,919],{"class":150},"# Compare two implementations statistically (eliminates noise):\n",[32,921,922],{"class":34,"line":46},[32,923,924],{"class":150},"# 1. Benchmark the old version:\n",[32,926,927,930],{"class":34,"line":53},[32,928,929],{"class":42},"git",[32,931,932],{"class":59}," stash\n",[32,934,935,937,940,943,946,949,952],{"class":34,"line":69},[32,936,21],{"class":42},[32,938,939],{"class":59}," test",[32,941,942],{"class":184}," -bench=BenchmarkValidate",[32,944,945],{"class":184}," -benchmem",[32,947,948],{"class":184}," -count=10",[32,950,951],{"class":38}," >",[32,953,954],{"class":59}," old.txt\n",[32,956,957],{"class":34,"line":74},[32,958,50],{"emptyLinePlaceholder":49},[32,960,961],{"class":34,"line":105},[32,962,963],{"class":150},"# 2. Benchmark the new version:\n",[32,965,966,968,971],{"class":34,"line":123},[32,967,929],{"class":42},[32,969,970],{"class":59}," stash",[32,972,973],{"class":59}," pop\n",[32,975,976,978,980,982,984,986,988],{"class":34,"line":132},[32,977,21],{"class":42},[32,979,939],{"class":59},[32,981,942],{"class":184},[32,983,945],{"class":184},[32,985,948],{"class":184},[32,987,951],{"class":38},[32,989,990],{"class":59}," new.txt\n",[32,992,993],{"class":34,"line":141},[32,994,50],{"emptyLinePlaceholder":49},[32,996,997],{"class":34,"line":154},[32,998,999],{"class":150},"# 3. Compare:\n",[32,1001,1002,1004,1007],{"class":34,"line":160},[32,1003,905],{"class":42},[32,1005,1006],{"class":59}," old.txt",[32,1008,990],{"class":59},[32,1010,1011],{"class":34,"line":197},[32,1012,1013],{"class":150},"# goos: darwin\n",[32,1015,1016],{"class":34,"line":221},[32,1017,1018],{"class":150},"# goarch: arm64\n",[32,1020,1021],{"class":34,"line":249},[32,1022,1023],{"class":150},"#                      │   old.txt   │               new.txt               │\n",[32,1025,1026],{"class":34,"line":280},[32,1027,1028],{"class":150},"#                      │   sec\u002Fop    │  sec\u002Fop     vs base                │\n",[32,1030,1031],{"class":34,"line":311},[32,1032,1033],{"class":150},"# Validate-8             112.2n ± 1%   85.4n ± 2%  -23.89% (p=0.000 n=10)\n",[32,1035,1036],{"class":34,"line":317},[32,1037,1038],{"class":150},"#\n",[32,1040,1041],{"class":34,"line":322},[32,1042,1043],{"class":150},"# The p-value \u003C 0.05 means the difference is statistically significant.\n",[32,1045,1046],{"class":34,"line":339},[32,1047,1048],{"class":150},"# \"vs base -23.89%\" means the new version is 23.89% faster.\n",[14,1050,1052],{"id":1051},"fuzzing-go-118","Fuzzing — Go 1.18+",[19,1054,1055,1351,1358],{"language":21},[23,1056,1058],{"className":25,"code":1057,"language":21,"meta":27,"style":27},"package parser\n\nimport \"testing\"\n\n\u002F\u002F Fuzz target — the engine generates random inputs to find panics.\nfunc FuzzParse(f *testing.F) {\n    \u002F\u002F Seed corpus — initial examples for the fuzzer to mutate:\n    f.Add(\"hello\")\n    f.Add(\"\")\n    f.Add(\"123\")\n    f.Add(\"hello world 123\")\n\n    f.Fuzz(func(t *testing.T, input string) {\n        \u002F\u002F The function should not panic on any input:\n        result, err := Parse(input)\n        if err != nil {\n            return  \u002F\u002F errors are fine — we're looking for panics\n        }\n\n        \u002F\u002F Invariant: Parse(result) should give back something valid:\n        if result != \"\" && result != input {\n            t.Errorf(\"Parse(%q) = %q, round-trip mismatch\", input, result)\n        }\n    })\n}\n\n\u002F\u002F Run:\n\u002F\u002F   go test -fuzz=FuzzParse -fuzztime=1m\n\u002F\u002F   go test -fuzz=FuzzParse -fuzztime=30m  # longer for more coverage\n\u002F\u002F\n\u002F\u002F Fuzz findings are saved to testdata\u002Ffuzz\u002FFuzzParse\u002F\u003Chash> — these\n\u002F\u002F become regression tests automatically (run by `go test`).\n",[29,1059,1060,1067,1071,1081,1085,1090,1113,1118,1134,1146,1159,1172,1176,1209,1214,1227,1240,1248,1253,1257,1262,1283,1306,1310,1315,1319,1323,1327,1332,1337,1341,1346],{"__ignoreMap":27},[32,1061,1062,1064],{"class":34,"line":35},[32,1063,39],{"class":38},[32,1065,1066],{"class":42}," parser\n",[32,1068,1069],{"class":34,"line":46},[32,1070,50],{"emptyLinePlaceholder":49},[32,1072,1073,1075,1077,1079],{"class":34,"line":53},[32,1074,56],{"class":38},[32,1076,60],{"class":59},[32,1078,63],{"class":42},[32,1080,66],{"class":59},[32,1082,1083],{"class":34,"line":69},[32,1084,50],{"emptyLinePlaceholder":49},[32,1086,1087],{"class":34,"line":74},[32,1088,1089],{"class":150},"\u002F\u002F Fuzz target — the engine generates random inputs to find panics.\n",[32,1091,1092,1094,1097,1099,1102,1104,1106,1108,1111],{"class":34,"line":105},[32,1093,77],{"class":38},[32,1095,1096],{"class":42}," FuzzParse",[32,1098,84],{"class":83},[32,1100,1101],{"class":87},"f",[32,1103,91],{"class":38},[32,1105,63],{"class":42},[32,1107,96],{"class":83},[32,1109,1110],{"class":42},"F",[32,1112,102],{"class":83},[32,1114,1115],{"class":34,"line":123},[32,1116,1117],{"class":150},"    \u002F\u002F Seed corpus — initial examples for the fuzzer to mutate:\n",[32,1119,1120,1123,1126,1128,1131],{"class":34,"line":132},[32,1121,1122],{"class":83},"    f.",[32,1124,1125],{"class":42},"Add",[32,1127,84],{"class":83},[32,1129,1130],{"class":59},"\"hello\"",[32,1132,1133],{"class":83},")\n",[32,1135,1136,1138,1140,1142,1144],{"class":34,"line":141},[32,1137,1122],{"class":83},[32,1139,1125],{"class":42},[32,1141,84],{"class":83},[32,1143,191],{"class":59},[32,1145,1133],{"class":83},[32,1147,1148,1150,1152,1154,1157],{"class":34,"line":154},[32,1149,1122],{"class":83},[32,1151,1125],{"class":42},[32,1153,84],{"class":83},[32,1155,1156],{"class":59},"\"123\"",[32,1158,1133],{"class":83},[32,1160,1161,1163,1165,1167,1170],{"class":34,"line":160},[32,1162,1122],{"class":83},[32,1164,1125],{"class":42},[32,1166,84],{"class":83},[32,1168,1169],{"class":59},"\"hello world 123\"",[32,1171,1133],{"class":83},[32,1173,1174],{"class":34,"line":197},[32,1175,50],{"emptyLinePlaceholder":49},[32,1177,1178,1180,1183,1185,1187,1189,1191,1193,1195,1197,1199,1201,1204,1207],{"class":34,"line":221},[32,1179,1122],{"class":83},[32,1181,1182],{"class":42},"Fuzz",[32,1184,84],{"class":83},[32,1186,77],{"class":38},[32,1188,84],{"class":83},[32,1190,88],{"class":87},[32,1192,91],{"class":38},[32,1194,63],{"class":42},[32,1196,96],{"class":83},[32,1198,99],{"class":42},[32,1200,169],{"class":83},[32,1202,1203],{"class":87},"input",[32,1205,1206],{"class":38}," string",[32,1208,102],{"class":83},[32,1210,1211],{"class":34,"line":249},[32,1212,1213],{"class":150},"        \u002F\u002F The function should not panic on any input:\n",[32,1215,1216,1219,1221,1224],{"class":34,"line":280},[32,1217,1218],{"class":83},"        result, err ",[32,1220,111],{"class":38},[32,1222,1223],{"class":42}," Parse",[32,1225,1226],{"class":83},"(input)\n",[32,1228,1229,1232,1234,1236,1238],{"class":34,"line":311},[32,1230,1231],{"class":38},"        if",[32,1233,424],{"class":83},[32,1235,427],{"class":38},[32,1237,430],{"class":184},[32,1239,120],{"class":83},[32,1241,1242,1245],{"class":34,"line":317},[32,1243,1244],{"class":38},"            return",[32,1246,1247],{"class":150},"  \u002F\u002F errors are fine — we're looking for panics\n",[32,1249,1250],{"class":34,"line":322},[32,1251,1252],{"class":83},"        }\n",[32,1254,1255],{"class":34,"line":339},[32,1256,50],{"emptyLinePlaceholder":49},[32,1258,1259],{"class":34,"line":367},[32,1260,1261],{"class":150},"        \u002F\u002F Invariant: Parse(result) should give back something valid:\n",[32,1263,1264,1266,1269,1271,1273,1276,1278,1280],{"class":34,"line":382},[32,1265,1231],{"class":38},[32,1267,1268],{"class":83}," result ",[32,1270,427],{"class":38},[32,1272,413],{"class":59},[32,1274,1275],{"class":38}," &&",[32,1277,1268],{"class":83},[32,1279,427],{"class":38},[32,1281,1282],{"class":83}," input {\n",[32,1284,1285,1287,1289,1291,1294,1296,1298,1300,1303],{"class":34,"line":387},[32,1286,370],{"class":83},[32,1288,441],{"class":42},[32,1290,84],{"class":83},[32,1292,1293],{"class":59},"\"Parse(",[32,1295,510],{"class":184},[32,1297,452],{"class":59},[32,1299,510],{"class":184},[32,1301,1302],{"class":59},", round-trip mismatch\"",[32,1304,1305],{"class":83},", input, result)\n",[32,1307,1308],{"class":34,"line":401},[32,1309,1252],{"class":83},[32,1311,1312],{"class":34,"line":418},[32,1313,1314],{"class":83},"    })\n",[32,1316,1317],{"class":34,"line":435},[32,1318,605],{"class":83},[32,1320,1321],{"class":34,"line":464},[32,1322,50],{"emptyLinePlaceholder":49},[32,1324,1325],{"class":34,"line":470},[32,1326,839],{"class":150},[32,1328,1329],{"class":34,"line":481},[32,1330,1331],{"class":150},"\u002F\u002F   go test -fuzz=FuzzParse -fuzztime=1m\n",[32,1333,1334],{"class":34,"line":494},[32,1335,1336],{"class":150},"\u002F\u002F   go test -fuzz=FuzzParse -fuzztime=30m  # longer for more coverage\n",[32,1338,1339],{"class":34,"line":519},[32,1340,849],{"class":150},[32,1342,1343],{"class":34,"line":548},[32,1344,1345],{"class":150},"\u002F\u002F Fuzz findings are saved to testdata\u002Ffuzz\u002FFuzzParse\u002F\u003Chash> — these\n",[32,1347,1348],{"class":34,"line":580},[32,1349,1350],{"class":150},"\u002F\u002F become regression tests automatically (run by `go test`).\n",[14,1352,1354,1357],{"id":1353},"httptest-testing-http-handlers",[29,1355,1356],{},"httptest"," — Testing HTTP Handlers",[19,1359,1360,1940,1944],{"language":21},[23,1361,1363],{"className":25,"code":1362,"language":21,"meta":27,"style":27},"package handler\n\nimport (\n    \"net\u002Fhttp\"\n    \"net\u002Fhttp\u002Fhttptest\"\n    \"testing\"\n)\n\nfunc TestGetUser(t *testing.T) {\n    \u002F\u002F Create a mock store (interface-based — see chapter 12):\n    store := &MockUserStore{users: map[int64]*User{1: {ID: 1, Name: \"Alice\"}}}\n    handler := NewUserHandler(store)\n\n    \u002F\u002F Build a request:\n    req := httptest.NewRequest(\"GET\", \"\u002Fusers\u002F1\", nil)\n    req = req.WithContext(context.Background())\n\n    \u002F\u002F Record the response (no real HTTP server):\n    rr := httptest.NewRecorder()\n\n    \u002F\u002F Call the handler:\n    handler.GetUser(rr, req)\n\n    \u002F\u002F Assert:\n    if rr.Code != http.StatusOK {\n        t.Errorf(\"status = %d, want %d\", rr.Code, http.StatusOK)\n    }\n\n    \u002F\u002F Check the body:\n    body := strings.TrimSpace(rr.Body.String())\n    if !strings.Contains(body, `\"name\":\"Alice\"`) {\n        t.Errorf(\"body = %q, want name Alice\", body)\n    }\n\n    \u002F\u002F Check headers:\n    if ct := rr.Header().Get(\"Content-Type\"); ct != \"application\u002Fjson\" {\n        t.Errorf(\"Content-Type = %q, want application\u002Fjson\", ct)\n    }\n}\n\n\u002F\u002F ─── Testing a full HTTP server ───\nfunc TestServerIntegration(t *testing.T) {\n    srv := httptest.NewServer(NewHandler())\n    defer srv.Close()\n\n    resp, err := http.Get(srv.URL + \"\u002Fusers\u002F1\")\n    if err != nil { t.Fatal(err) }\n    defer resp.Body.Close()\n    \u002F\u002F assert on resp\n}\n",[29,1364,1365,1372,1376,1383,1393,1402,1410,1414,1418,1439,1444,1496,1509,1513,1518,1548,1569,1573,1578,1593,1597,1602,1613,1617,1622,1635,1659,1663,1667,1672,1693,1711,1730,1734,1738,1743,1779,1798,1802,1806,1810,1815,1836,1855,1868,1873,1897,1917,1929,1935],{"__ignoreMap":27},[32,1366,1367,1369],{"class":34,"line":35},[32,1368,39],{"class":38},[32,1370,1371],{"class":42}," handler\n",[32,1373,1374],{"class":34,"line":46},[32,1375,50],{"emptyLinePlaceholder":49},[32,1377,1378,1380],{"class":34,"line":53},[32,1379,56],{"class":38},[32,1381,1382],{"class":83}," (\n",[32,1384,1385,1388,1391],{"class":34,"line":69},[32,1386,1387],{"class":59},"    \"",[32,1389,1390],{"class":42},"net\u002Fhttp",[32,1392,66],{"class":59},[32,1394,1395,1397,1400],{"class":34,"line":74},[32,1396,1387],{"class":59},[32,1398,1399],{"class":42},"net\u002Fhttp\u002Fhttptest",[32,1401,66],{"class":59},[32,1403,1404,1406,1408],{"class":34,"line":105},[32,1405,1387],{"class":59},[32,1407,63],{"class":42},[32,1409,66],{"class":59},[32,1411,1412],{"class":34,"line":123},[32,1413,1133],{"class":83},[32,1415,1416],{"class":34,"line":132},[32,1417,50],{"emptyLinePlaceholder":49},[32,1419,1420,1422,1425,1427,1429,1431,1433,1435,1437],{"class":34,"line":141},[32,1421,77],{"class":38},[32,1423,1424],{"class":42}," TestGetUser",[32,1426,84],{"class":83},[32,1428,88],{"class":87},[32,1430,91],{"class":38},[32,1432,63],{"class":42},[32,1434,96],{"class":83},[32,1436,99],{"class":42},[32,1438,102],{"class":83},[32,1440,1441],{"class":34,"line":154},[32,1442,1443],{"class":150},"    \u002F\u002F Create a mock store (interface-based — see chapter 12):\n",[32,1445,1446,1449,1451,1454,1457,1460,1463,1466,1469,1472,1475,1477,1480,1482,1485,1487,1490,1493],{"class":34,"line":160},[32,1447,1448],{"class":83},"    store ",[32,1450,111],{"class":38},[32,1452,1453],{"class":38}," &",[32,1455,1456],{"class":42},"MockUserStore",[32,1458,1459],{"class":83},"{users: ",[32,1461,1462],{"class":38},"map",[32,1464,1465],{"class":83},"[",[32,1467,1468],{"class":38},"int64",[32,1470,1471],{"class":83},"]",[32,1473,1474],{"class":38},"*",[32,1476,172],{"class":42},[32,1478,1479],{"class":83},"{",[32,1481,270],{"class":184},[32,1483,1484],{"class":83},": {ID: ",[32,1486,270],{"class":184},[32,1488,1489],{"class":83},", Name: ",[32,1491,1492],{"class":59},"\"Alice\"",[32,1494,1495],{"class":83},"}}}\n",[32,1497,1498,1501,1503,1506],{"class":34,"line":197},[32,1499,1500],{"class":83},"    handler ",[32,1502,111],{"class":38},[32,1504,1505],{"class":42}," NewUserHandler",[32,1507,1508],{"class":83},"(store)\n",[32,1510,1511],{"class":34,"line":221},[32,1512,50],{"emptyLinePlaceholder":49},[32,1514,1515],{"class":34,"line":249},[32,1516,1517],{"class":150},"    \u002F\u002F Build a request:\n",[32,1519,1520,1523,1525,1528,1531,1533,1536,1538,1541,1543,1546],{"class":34,"line":280},[32,1521,1522],{"class":83},"    req ",[32,1524,111],{"class":38},[32,1526,1527],{"class":83}," httptest.",[32,1529,1530],{"class":42},"NewRequest",[32,1532,84],{"class":83},[32,1534,1535],{"class":59},"\"GET\"",[32,1537,169],{"class":83},[32,1539,1540],{"class":59},"\"\u002Fusers\u002F1\"",[32,1542,169],{"class":83},[32,1544,1545],{"class":184},"nil",[32,1547,1133],{"class":83},[32,1549,1550,1552,1554,1557,1560,1563,1566],{"class":34,"line":311},[32,1551,1522],{"class":83},[32,1553,814],{"class":38},[32,1555,1556],{"class":83}," req.",[32,1558,1559],{"class":42},"WithContext",[32,1561,1562],{"class":83},"(context.",[32,1564,1565],{"class":42},"Background",[32,1567,1568],{"class":83},"())\n",[32,1570,1571],{"class":34,"line":317},[32,1572,50],{"emptyLinePlaceholder":49},[32,1574,1575],{"class":34,"line":322},[32,1576,1577],{"class":150},"    \u002F\u002F Record the response (no real HTTP server):\n",[32,1579,1580,1583,1585,1587,1590],{"class":34,"line":339},[32,1581,1582],{"class":83},"    rr ",[32,1584,111],{"class":38},[32,1586,1527],{"class":83},[32,1588,1589],{"class":42},"NewRecorder",[32,1591,1592],{"class":83},"()\n",[32,1594,1595],{"class":34,"line":367},[32,1596,50],{"emptyLinePlaceholder":49},[32,1598,1599],{"class":34,"line":382},[32,1600,1601],{"class":150},"    \u002F\u002F Call the handler:\n",[32,1603,1604,1607,1610],{"class":34,"line":387},[32,1605,1606],{"class":83},"    handler.",[32,1608,1609],{"class":42},"GetUser",[32,1611,1612],{"class":83},"(rr, req)\n",[32,1614,1615],{"class":34,"line":401},[32,1616,50],{"emptyLinePlaceholder":49},[32,1618,1619],{"class":34,"line":418},[32,1620,1621],{"class":150},"    \u002F\u002F Assert:\n",[32,1623,1624,1627,1630,1632],{"class":34,"line":435},[32,1625,1626],{"class":38},"    if",[32,1628,1629],{"class":83}," rr.Code ",[32,1631,427],{"class":38},[32,1633,1634],{"class":83}," http.StatusOK {\n",[32,1636,1637,1639,1641,1643,1646,1649,1652,1654,1656],{"class":34,"line":464},[32,1638,342],{"class":83},[32,1640,441],{"class":42},[32,1642,84],{"class":83},[32,1644,1645],{"class":59},"\"status = ",[32,1647,1648],{"class":184},"%d",[32,1650,1651],{"class":59},", want ",[32,1653,1648],{"class":184},[32,1655,513],{"class":59},[32,1657,1658],{"class":83},", rr.Code, http.StatusOK)\n",[32,1660,1661],{"class":34,"line":470},[32,1662,314],{"class":83},[32,1664,1665],{"class":34,"line":481},[32,1666,50],{"emptyLinePlaceholder":49},[32,1668,1669],{"class":34,"line":494},[32,1670,1671],{"class":150},"    \u002F\u002F Check the body:\n",[32,1673,1674,1677,1679,1682,1685,1688,1691],{"class":34,"line":519},[32,1675,1676],{"class":83},"    body ",[32,1678,111],{"class":38},[32,1680,1681],{"class":83}," strings.",[32,1683,1684],{"class":42},"TrimSpace",[32,1686,1687],{"class":83},"(rr.Body.",[32,1689,1690],{"class":42},"String",[32,1692,1568],{"class":83},[32,1694,1695,1697,1699,1701,1703,1706,1709],{"class":34,"line":548},[32,1696,1626],{"class":38},[32,1698,530],{"class":38},[32,1700,533],{"class":83},[32,1702,536],{"class":42},[32,1704,1705],{"class":83},"(body, ",[32,1707,1708],{"class":59},"`\"name\":\"Alice\"`",[32,1710,102],{"class":83},[32,1712,1713,1715,1717,1719,1722,1724,1727],{"class":34,"line":580},[32,1714,342],{"class":83},[32,1716,441],{"class":42},[32,1718,84],{"class":83},[32,1720,1721],{"class":59},"\"body = ",[32,1723,510],{"class":184},[32,1725,1726],{"class":59},", want name Alice\"",[32,1728,1729],{"class":83},", body)\n",[32,1731,1732],{"class":34,"line":585},[32,1733,314],{"class":83},[32,1735,1736],{"class":34,"line":591},[32,1737,50],{"emptyLinePlaceholder":49},[32,1739,1740],{"class":34,"line":597},[32,1741,1742],{"class":150},"    \u002F\u002F Check headers:\n",[32,1744,1745,1747,1750,1752,1755,1758,1761,1764,1766,1769,1772,1774,1777],{"class":34,"line":602},[32,1746,1626],{"class":38},[32,1748,1749],{"class":83}," ct ",[32,1751,111],{"class":38},[32,1753,1754],{"class":83}," rr.",[32,1756,1757],{"class":42},"Header",[32,1759,1760],{"class":83},"().",[32,1762,1763],{"class":42},"Get",[32,1765,84],{"class":83},[32,1767,1768],{"class":59},"\"Content-Type\"",[32,1770,1771],{"class":83},"); ct ",[32,1773,427],{"class":38},[32,1775,1776],{"class":59}," \"application\u002Fjson\"",[32,1778,120],{"class":83},[32,1780,1781,1783,1785,1787,1790,1792,1795],{"class":34,"line":608},[32,1782,342],{"class":83},[32,1784,441],{"class":42},[32,1786,84],{"class":83},[32,1788,1789],{"class":59},"\"Content-Type = ",[32,1791,510],{"class":184},[32,1793,1794],{"class":59},", want application\u002Fjson\"",[32,1796,1797],{"class":83},", ct)\n",[32,1799,1800],{"class":34,"line":613},[32,1801,314],{"class":83},[32,1803,1804],{"class":34,"line":619},[32,1805,605],{"class":83},[32,1807,1808],{"class":34,"line":625},[32,1809,50],{"emptyLinePlaceholder":49},[32,1811,1812],{"class":34,"line":631},[32,1813,1814],{"class":150},"\u002F\u002F ─── Testing a full HTTP server ───\n",[32,1816,1817,1819,1822,1824,1826,1828,1830,1832,1834],{"class":34,"line":637},[32,1818,77],{"class":38},[32,1820,1821],{"class":42}," TestServerIntegration",[32,1823,84],{"class":83},[32,1825,88],{"class":87},[32,1827,91],{"class":38},[32,1829,63],{"class":42},[32,1831,96],{"class":83},[32,1833,99],{"class":42},[32,1835,102],{"class":83},[32,1837,1838,1841,1843,1845,1848,1850,1853],{"class":34,"line":643},[32,1839,1840],{"class":83},"    srv ",[32,1842,111],{"class":38},[32,1844,1527],{"class":83},[32,1846,1847],{"class":42},"NewServer",[32,1849,84],{"class":83},[32,1851,1852],{"class":42},"NewHandler",[32,1854,1568],{"class":83},[32,1856,1857,1860,1863,1866],{"class":34,"line":649},[32,1858,1859],{"class":38},"    defer",[32,1861,1862],{"class":83}," srv.",[32,1864,1865],{"class":42},"Close",[32,1867,1592],{"class":83},[32,1869,1871],{"class":34,"line":1870},45,[32,1872,50],{"emptyLinePlaceholder":49},[32,1874,1876,1879,1881,1884,1886,1889,1892,1895],{"class":34,"line":1875},46,[32,1877,1878],{"class":83},"    resp, err ",[32,1880,111],{"class":38},[32,1882,1883],{"class":83}," http.",[32,1885,1763],{"class":42},[32,1887,1888],{"class":83},"(srv.URL ",[32,1890,1891],{"class":38},"+",[32,1893,1894],{"class":59}," \"\u002Fusers\u002F1\"",[32,1896,1133],{"class":83},[32,1898,1900,1902,1904,1906,1908,1911,1914],{"class":34,"line":1899},47,[32,1901,1626],{"class":38},[32,1903,424],{"class":83},[32,1905,427],{"class":38},[32,1907,430],{"class":184},[32,1909,1910],{"class":83}," { t.",[32,1912,1913],{"class":42},"Fatal",[32,1915,1916],{"class":83},"(err) }\n",[32,1918,1920,1922,1925,1927],{"class":34,"line":1919},48,[32,1921,1859],{"class":38},[32,1923,1924],{"class":83}," resp.Body.",[32,1926,1865],{"class":42},[32,1928,1592],{"class":83},[32,1930,1932],{"class":34,"line":1931},49,[32,1933,1934],{"class":150},"    \u002F\u002F assert on resp\n",[32,1936,1938],{"class":34,"line":1937},50,[32,1939,605],{"class":83},[14,1941,1943],{"id":1942},"coverage-gating-ci","Coverage — Gating CI",[19,1945,1946,2186,2193],{"language":908},[23,1947,1949],{"className":911,"code":1948,"language":908,"meta":27,"style":27},"# Generate coverage profile:\ngo test -coverprofile=coverage.out .\u002F...\n\n# Summary (per function):\ngo tool cover -func=coverage.out\n\n# HTML report:\ngo tool cover -html=coverage.out -o coverage.html\n\n# ✅ CI gate — fail if any function has \u003C 80% coverage:\ngo test -coverprofile=c.out .\u002F...\ngo tool cover -func=c.out | grep -v 100.0% | grep -v \"total:\" && exit 1\n\n# ✅ Better — fail if total coverage \u003C threshold:\nTOTAL=$(go tool cover -func=c.out | grep total | awk '{print $3}' | tr -d '%')\nif (( $(echo \"$TOTAL \u003C 80\" | bc -l) )); then\n  echo \"Coverage $TOTAL% \u003C 80%\"\n  exit 1\nfi\n",[29,1950,1951,1956,1968,1972,1977,1990,1994,1999,2016,2020,2025,2036,2077,2081,2086,2132,2161,2174,2181],{"__ignoreMap":27},[32,1952,1953],{"class":34,"line":35},[32,1954,1955],{"class":150},"# Generate coverage profile:\n",[32,1957,1958,1960,1962,1965],{"class":34,"line":46},[32,1959,21],{"class":42},[32,1961,939],{"class":59},[32,1963,1964],{"class":184}," -coverprofile=coverage.out",[32,1966,1967],{"class":59}," .\u002F...\n",[32,1969,1970],{"class":34,"line":53},[32,1971,50],{"emptyLinePlaceholder":49},[32,1973,1974],{"class":34,"line":69},[32,1975,1976],{"class":150},"# Summary (per function):\n",[32,1978,1979,1981,1984,1987],{"class":34,"line":74},[32,1980,21],{"class":42},[32,1982,1983],{"class":59}," tool",[32,1985,1986],{"class":59}," cover",[32,1988,1989],{"class":184}," -func=coverage.out\n",[32,1991,1992],{"class":34,"line":105},[32,1993,50],{"emptyLinePlaceholder":49},[32,1995,1996],{"class":34,"line":123},[32,1997,1998],{"class":150},"# HTML report:\n",[32,2000,2001,2003,2005,2007,2010,2013],{"class":34,"line":132},[32,2002,21],{"class":42},[32,2004,1983],{"class":59},[32,2006,1986],{"class":59},[32,2008,2009],{"class":184}," -html=coverage.out",[32,2011,2012],{"class":184}," -o",[32,2014,2015],{"class":59}," coverage.html\n",[32,2017,2018],{"class":34,"line":141},[32,2019,50],{"emptyLinePlaceholder":49},[32,2021,2022],{"class":34,"line":154},[32,2023,2024],{"class":150},"# ✅ CI gate — fail if any function has \u003C 80% coverage:\n",[32,2026,2027,2029,2031,2034],{"class":34,"line":160},[32,2028,21],{"class":42},[32,2030,939],{"class":59},[32,2032,2033],{"class":184}," -coverprofile=c.out",[32,2035,1967],{"class":59},[32,2037,2038,2040,2042,2044,2047,2050,2053,2056,2059,2061,2063,2065,2068,2071,2074],{"class":34,"line":197},[32,2039,21],{"class":42},[32,2041,1983],{"class":59},[32,2043,1986],{"class":59},[32,2045,2046],{"class":184}," -func=c.out",[32,2048,2049],{"class":38}," |",[32,2051,2052],{"class":42}," grep",[32,2054,2055],{"class":184}," -v",[32,2057,2058],{"class":59}," 100.0%",[32,2060,2049],{"class":38},[32,2062,2052],{"class":42},[32,2064,2055],{"class":184},[32,2066,2067],{"class":59}," \"total:\"",[32,2069,2070],{"class":83}," && ",[32,2072,2073],{"class":184},"exit",[32,2075,2076],{"class":184}," 1\n",[32,2078,2079],{"class":34,"line":221},[32,2080,50],{"emptyLinePlaceholder":49},[32,2082,2083],{"class":34,"line":249},[32,2084,2085],{"class":150},"# ✅ Better — fail if total coverage \u003C threshold:\n",[32,2087,2088,2091,2093,2096,2098,2100,2102,2104,2106,2108,2111,2113,2116,2119,2121,2124,2127,2130],{"class":34,"line":280},[32,2089,2090],{"class":83},"TOTAL",[32,2092,814],{"class":38},[32,2094,2095],{"class":83},"$(",[32,2097,21],{"class":42},[32,2099,1983],{"class":59},[32,2101,1986],{"class":59},[32,2103,2046],{"class":184},[32,2105,2049],{"class":38},[32,2107,2052],{"class":42},[32,2109,2110],{"class":59}," total",[32,2112,2049],{"class":38},[32,2114,2115],{"class":42}," awk",[32,2117,2118],{"class":59}," '{print $3}'",[32,2120,2049],{"class":38},[32,2122,2123],{"class":42}," tr",[32,2125,2126],{"class":184}," -d",[32,2128,2129],{"class":59}," '%'",[32,2131,1133],{"class":83},[32,2133,2134,2137,2140,2142,2145,2148,2150,2153,2155,2158],{"class":34,"line":311},[32,2135,2136],{"class":38},"if",[32,2138,2139],{"class":83}," (( $(echo ",[32,2141,513],{"class":59},[32,2143,2144],{"class":83},"$TOTAL",[32,2146,2147],{"class":59}," \u003C 80\"",[32,2149,2049],{"class":38},[32,2151,2152],{"class":83}," bc ",[32,2154,267],{"class":38},[32,2156,2157],{"class":83},"l) )); ",[32,2159,2160],{"class":38},"then\n",[32,2162,2163,2166,2169,2171],{"class":34,"line":317},[32,2164,2165],{"class":184},"  echo",[32,2167,2168],{"class":59}," \"Coverage ",[32,2170,2144],{"class":83},[32,2172,2173],{"class":59},"% \u003C 80%\"\n",[32,2175,2176,2179],{"class":34,"line":322},[32,2177,2178],{"class":184},"  exit",[32,2180,2076],{"class":184},[32,2182,2183],{"class":34,"line":339},[32,2184,2185],{"class":38},"fi\n",[14,2187,2189,2190],{"id":2188},"test-helpers-thelper","Test Helpers — ",[29,2191,2192],{},"t.Helper",[19,2194,2195,2419,2426],{"language":21},[23,2196,2198],{"className":25,"code":2197,"language":21,"meta":27,"style":27},"\u002F\u002F t.Helper() marks a function as a test helper — its line is excluded\n\u002F\u002F from error output, pointing to the CALLER's line instead.\n\nfunc assertUserEqual(t *testing.T, got, want *User) {\n    t.Helper()  \u002F\u002F ✅ errors point to the caller, not this helper\n    if got.ID != want.ID {\n        t.Errorf(\"ID = %d, want %d\", got.ID, want.ID)\n    }\n    if got.Name != want.Name {\n        t.Errorf(\"Name = %q, want %q\", got.Name, want.Name)\n    }\n}\n\nfunc TestGetUser(t *testing.T) {\n    got := getUser(1)\n    want := &User{ID: 1, Name: \"Alice\"}\n    assertUserEqual(t, got, want)  \u002F\u002F error points HERE, not to assertUserEqual\n}\n",[29,2199,2200,2205,2210,2214,2249,2262,2274,2296,2300,2312,2334,2338,2342,2346,2366,2382,2404,2415],{"__ignoreMap":27},[32,2201,2202],{"class":34,"line":35},[32,2203,2204],{"class":150},"\u002F\u002F t.Helper() marks a function as a test helper — its line is excluded\n",[32,2206,2207],{"class":34,"line":46},[32,2208,2209],{"class":150},"\u002F\u002F from error output, pointing to the CALLER's line instead.\n",[32,2211,2212],{"class":34,"line":53},[32,2213,50],{"emptyLinePlaceholder":49},[32,2215,2216,2218,2221,2223,2225,2227,2229,2231,2233,2235,2238,2240,2243,2245,2247],{"class":34,"line":69},[32,2217,77],{"class":38},[32,2219,2220],{"class":42}," assertUserEqual",[32,2222,84],{"class":83},[32,2224,88],{"class":87},[32,2226,91],{"class":38},[32,2228,63],{"class":42},[32,2230,96],{"class":83},[32,2232,99],{"class":42},[32,2234,169],{"class":83},[32,2236,2237],{"class":87},"got",[32,2239,169],{"class":83},[32,2241,2242],{"class":87},"want",[32,2244,91],{"class":38},[32,2246,172],{"class":42},[32,2248,102],{"class":83},[32,2250,2251,2254,2257,2259],{"class":34,"line":74},[32,2252,2253],{"class":83},"    t.",[32,2255,2256],{"class":42},"Helper",[32,2258,376],{"class":83},[32,2260,2261],{"class":150},"\u002F\u002F ✅ errors point to the caller, not this helper\n",[32,2263,2264,2266,2269,2271],{"class":34,"line":105},[32,2265,1626],{"class":38},[32,2267,2268],{"class":83}," got.ID ",[32,2270,427],{"class":38},[32,2272,2273],{"class":83}," want.ID {\n",[32,2275,2276,2278,2280,2282,2285,2287,2289,2291,2293],{"class":34,"line":123},[32,2277,342],{"class":83},[32,2279,441],{"class":42},[32,2281,84],{"class":83},[32,2283,2284],{"class":59},"\"ID = ",[32,2286,1648],{"class":184},[32,2288,1651],{"class":59},[32,2290,1648],{"class":184},[32,2292,513],{"class":59},[32,2294,2295],{"class":83},", got.ID, want.ID)\n",[32,2297,2298],{"class":34,"line":132},[32,2299,314],{"class":83},[32,2301,2302,2304,2307,2309],{"class":34,"line":141},[32,2303,1626],{"class":38},[32,2305,2306],{"class":83}," got.Name ",[32,2308,427],{"class":38},[32,2310,2311],{"class":83}," want.Name {\n",[32,2313,2314,2316,2318,2320,2323,2325,2327,2329,2331],{"class":34,"line":154},[32,2315,342],{"class":83},[32,2317,441],{"class":42},[32,2319,84],{"class":83},[32,2321,2322],{"class":59},"\"Name = ",[32,2324,510],{"class":184},[32,2326,1651],{"class":59},[32,2328,510],{"class":184},[32,2330,513],{"class":59},[32,2332,2333],{"class":83},", got.Name, want.Name)\n",[32,2335,2336],{"class":34,"line":160},[32,2337,314],{"class":83},[32,2339,2340],{"class":34,"line":197},[32,2341,605],{"class":83},[32,2343,2344],{"class":34,"line":221},[32,2345,50],{"emptyLinePlaceholder":49},[32,2347,2348,2350,2352,2354,2356,2358,2360,2362,2364],{"class":34,"line":249},[32,2349,77],{"class":38},[32,2351,1424],{"class":42},[32,2353,84],{"class":83},[32,2355,88],{"class":87},[32,2357,91],{"class":38},[32,2359,63],{"class":42},[32,2361,96],{"class":83},[32,2363,99],{"class":42},[32,2365,102],{"class":83},[32,2367,2368,2371,2373,2376,2378,2380],{"class":34,"line":280},[32,2369,2370],{"class":83},"    got ",[32,2372,111],{"class":38},[32,2374,2375],{"class":42}," getUser",[32,2377,84],{"class":83},[32,2379,270],{"class":184},[32,2381,1133],{"class":83},[32,2383,2384,2387,2389,2391,2393,2396,2398,2400,2402],{"class":34,"line":311},[32,2385,2386],{"class":83},"    want ",[32,2388,111],{"class":38},[32,2390,1453],{"class":38},[32,2392,172],{"class":42},[32,2394,2395],{"class":83},"{ID: ",[32,2397,270],{"class":184},[32,2399,1489],{"class":83},[32,2401,1492],{"class":59},[32,2403,605],{"class":83},[32,2405,2406,2409,2412],{"class":34,"line":317},[32,2407,2408],{"class":42},"    assertUserEqual",[32,2410,2411],{"class":83},"(t, got, want)  ",[32,2413,2414],{"class":150},"\u002F\u002F error points HERE, not to assertUserEqual\n",[32,2416,2417],{"class":34,"line":322},[32,2418,605],{"class":83},[14,2420,2422,2425],{"id":2421},"testify-assertions-library-optional",[29,2423,2424],{},"testify"," — Assertions Library (Optional)",[19,2427,2428,2616,2620,2703,2707,2820,2824],{"language":21},[23,2429,2431],{"className":25,"code":2430,"language":21,"meta":27,"style":27},"\u002F\u002F import \"github.com\u002Fstretchr\u002Ftestify\u002Fassert\"\n\u002F\u002F import \"github.com\u002Fstretchr\u002Ftestify\u002Frequire\"\n\nfunc TestWithTestify(t *testing.T) {\n    \u002F\u002F assert — reports failure, continues:\n    assert.Equal(t, 5, Add(2, 3))\n    assert.NoError(t, err)\n    assert.NotNil(t, user)\n    assert.Contains(t, \"hello world\", \"world\")\n\n    \u002F\u002F require — reports failure, STOPS the test (for fatal setup):\n    require.NoError(t, err, \"database connection failed\")\n    \u002F\u002F If err != nil, test stops here — don't continue with nil db\n    user := getUser(1)\n    assert.Equal(t, \"Alice\", user.Name)\n}\n\n\u002F\u002F testify is popular but optional — stdlib `t.Errorf` is fine.\n\u002F\u002F Use testify for readability in large test suites.\n",[29,2432,2433,2438,2443,2447,2468,2473,2504,2514,2524,2542,2546,2551,2566,2571,2585,2598,2602,2606,2611],{"__ignoreMap":27},[32,2434,2435],{"class":34,"line":35},[32,2436,2437],{"class":150},"\u002F\u002F import \"github.com\u002Fstretchr\u002Ftestify\u002Fassert\"\n",[32,2439,2440],{"class":34,"line":46},[32,2441,2442],{"class":150},"\u002F\u002F import \"github.com\u002Fstretchr\u002Ftestify\u002Frequire\"\n",[32,2444,2445],{"class":34,"line":53},[32,2446,50],{"emptyLinePlaceholder":49},[32,2448,2449,2451,2454,2456,2458,2460,2462,2464,2466],{"class":34,"line":69},[32,2450,77],{"class":38},[32,2452,2453],{"class":42}," TestWithTestify",[32,2455,84],{"class":83},[32,2457,88],{"class":87},[32,2459,91],{"class":38},[32,2461,63],{"class":42},[32,2463,96],{"class":83},[32,2465,99],{"class":42},[32,2467,102],{"class":83},[32,2469,2470],{"class":34,"line":74},[32,2471,2472],{"class":150},"    \u002F\u002F assert — reports failure, continues:\n",[32,2474,2475,2478,2481,2484,2487,2489,2491,2493,2496,2498,2501],{"class":34,"line":105},[32,2476,2477],{"class":83},"    assert.",[32,2479,2480],{"class":42},"Equal",[32,2482,2483],{"class":83},"(t, ",[32,2485,2486],{"class":184},"5",[32,2488,169],{"class":83},[32,2490,1125],{"class":42},[32,2492,84],{"class":83},[32,2494,2495],{"class":184},"2",[32,2497,169],{"class":83},[32,2499,2500],{"class":184},"3",[32,2502,2503],{"class":83},"))\n",[32,2505,2506,2508,2511],{"class":34,"line":123},[32,2507,2477],{"class":83},[32,2509,2510],{"class":42},"NoError",[32,2512,2513],{"class":83},"(t, err)\n",[32,2515,2516,2518,2521],{"class":34,"line":132},[32,2517,2477],{"class":83},[32,2519,2520],{"class":42},"NotNil",[32,2522,2523],{"class":83},"(t, user)\n",[32,2525,2526,2528,2530,2532,2535,2537,2540],{"class":34,"line":141},[32,2527,2477],{"class":83},[32,2529,536],{"class":42},[32,2531,2483],{"class":83},[32,2533,2534],{"class":59},"\"hello world\"",[32,2536,169],{"class":83},[32,2538,2539],{"class":59},"\"world\"",[32,2541,1133],{"class":83},[32,2543,2544],{"class":34,"line":154},[32,2545,50],{"emptyLinePlaceholder":49},[32,2547,2548],{"class":34,"line":160},[32,2549,2550],{"class":150},"    \u002F\u002F require — reports failure, STOPS the test (for fatal setup):\n",[32,2552,2553,2556,2558,2561,2564],{"class":34,"line":197},[32,2554,2555],{"class":83},"    require.",[32,2557,2510],{"class":42},[32,2559,2560],{"class":83},"(t, err, ",[32,2562,2563],{"class":59},"\"database connection failed\"",[32,2565,1133],{"class":83},[32,2567,2568],{"class":34,"line":221},[32,2569,2570],{"class":150},"    \u002F\u002F If err != nil, test stops here — don't continue with nil db\n",[32,2572,2573,2575,2577,2579,2581,2583],{"class":34,"line":249},[32,2574,729],{"class":83},[32,2576,111],{"class":38},[32,2578,2375],{"class":42},[32,2580,84],{"class":83},[32,2582,270],{"class":184},[32,2584,1133],{"class":83},[32,2586,2587,2589,2591,2593,2595],{"class":34,"line":280},[32,2588,2477],{"class":83},[32,2590,2480],{"class":42},[32,2592,2483],{"class":83},[32,2594,1492],{"class":59},[32,2596,2597],{"class":83},", user.Name)\n",[32,2599,2600],{"class":34,"line":311},[32,2601,605],{"class":83},[32,2603,2604],{"class":34,"line":317},[32,2605,50],{"emptyLinePlaceholder":49},[32,2607,2608],{"class":34,"line":322},[32,2609,2610],{"class":150},"\u002F\u002F testify is popular but optional — stdlib `t.Errorf` is fine.\n",[32,2612,2613],{"class":34,"line":339},[32,2614,2615],{"class":150},"\u002F\u002F Use testify for readability in large test suites.\n",[14,2617,2619],{"id":2618},"tips-tricks","💡 Tips & Tricks",[2621,2622,2623,2643,2656,2668,2681,2689],"ul",{},[2624,2625,2626,2630,2631,2634,2635,2638,2639,2642],"li",{},[2627,2628,2629],"strong",{},"Idiom",": table-driven tests with ",[29,2632,2633],{},"t.Run"," and ",[29,2636,2637],{},"t.Parallel"," — each case is a subtest (filterable with ",[29,2640,2641],{},"-run","), runs concurrently (faster). Include edge cases: zero, negative, empty, boundary values.",[2624,2644,2645,2647,2648,2651,2652,2655],{},[2627,2646,2629],{},": ",[29,2649,2650],{},"b.ReportAllocs()"," + ",[29,2653,2654],{},"b.ResetTimer()"," in benchmarks — allocs\u002Fop is the key metric (heap allocations → GC pressure). ResetTimer excludes setup from timing.",[2624,2657,2658,2660,2661,2663,2664,2667],{},[2627,2659,2629],{},": use ",[29,2662,905],{}," to compare benchmark results statistically — run ",[29,2665,2666],{},"-count=10"," for both versions, benchstat gives a p-value. Differences \u003C 5% are noise.",[2624,2669,2670,2672,2673,2676,2677,2680],{},[2627,2671,2629],{},": fuzz targets find panics the test suite misses — ",[29,2674,2675],{},"f.Add(seed)"," for initial cases, ",[29,2678,2679],{},"f.Fuzz"," for the test. Findings auto-save as regression tests.",[2624,2682,2683,2647,2685,2688],{},[2627,2684,2629],{},[29,2686,2687],{},"t.Helper()"," in test helpers — error line numbers point to the caller, not the helper. Makes debugging easier.",[2624,2690,2691,2647,2693,2651,2696,2699,2700,96],{},[2627,2692,2629],{},[29,2694,2695],{},"httptest.NewRequest",[29,2697,2698],{},"httptest.NewRecorder"," for testing HTTP handlers without a real server — fast, no port allocation, no network. For full integration, ",[29,2701,2702],{},"httptest.NewServer",[14,2704,2706],{"id":2705},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[2621,2708,2709,2725,2737,2753,2769,2779,2785,2801],{},[2624,2710,2711,2717,2718,2720,2721,2724],{},[2627,2712,2713,2716],{},[29,2714,2715],{},"go test"," caches results",": if nothing changed, ",[29,2719,2715],{}," prints \"ok (cached)\" and doesn't re-run. Use ",[29,2722,2723],{},"-count=1"," to force re-run (essential for flaky tests).",[2624,2726,2727,2733,2734,96],{},[2627,2728,2729,2732],{},[29,2730,2731],{},"b.N"," is framework-controlled",": don't set it yourself — the framework adjusts it. Just use ",[29,2735,2736],{},"for i := 0; i \u003C b.N; i++",[2624,2738,2739,2647,2742,2745,2746,2634,2749,2752],{},[2627,2740,2741],{},"Compiler optimizes away the result",[29,2743,2744],{},"_ = Validate(user)"," — if the compiler sees the result is unused, it may skip the call. Use a package-level ",[29,2747,2748],{},"var sink int",[29,2750,2751],{},"sink = result"," to prevent optimization.",[2624,2754,2755,2761,2762,2764,2765,2768],{},[2627,2756,2757,2760],{},[29,2758,2759],{},"t.Parallel()"," inside subtests",": call it first inside ",[29,2763,2633],{},". Pre-1.22, capture the loop variable (",[29,2766,2767],{},"tt := tt",") before the parallel subtest.",[2624,2770,2771,2776,2777,96],{},[2627,2772,2773,2775],{},[29,2774,2698],{}," doesn't support real HTTP",": it records what the handler writes — no real connection, no client disconnect. For real HTTP, use ",[29,2778,2702],{},[2624,2780,2781,2784],{},[2627,2782,2783],{},"Coverage doesn't test all paths",": 100% coverage means every line was executed, not that every branch was tested. Use fuzzing and property tests for deeper coverage.",[2624,2786,2787,2660,2793,2796,2797,2800],{},[2627,2788,2789,2792],{},[29,2790,2791],{},"testify\u002Frequire"," stops the test",[29,2794,2795],{},"require"," for setup (if DB connection fails, stop). Use ",[29,2798,2799],{},"assert"," for checks (if one assertion fails, continue checking others).",[2624,2802,2803,2809,2810,2812,2813,2816,2817,2819],{},[2627,2804,2805,2806],{},"Fuzz findings saved to ",[29,2807,2808],{},"testdata\u002F",": the ",[29,2811,2808],{}," directory is excluded from ",[29,2814,2815],{},"go build"," but included in ",[29,2818,2715],{},". Don't put test-only code anywhere else.",[14,2821,2823],{"id":2822},"quick-quiz","🧠 Quick Quiz",[19,2825,2826,2904],{"language":21},[23,2827,2829],{"className":25,"code":2828,"language":21,"meta":27,"style":27},"func BenchmarkBad(b *testing.B) {\n    user := User{Email: \"test@example.com\"}\n    for i := 0; i \u003C b.N; i++ {\n        Validate(user)  \u002F\u002F result discarded\n    }\n}\n",[29,2830,2831,2852,2866,2886,2896,2900],{"__ignoreMap":27},[32,2832,2833,2835,2838,2840,2842,2844,2846,2848,2850],{"class":34,"line":35},[32,2834,77],{"class":38},[32,2836,2837],{"class":42}," BenchmarkBad",[32,2839,84],{"class":83},[32,2841,699],{"class":87},[32,2843,91],{"class":38},[32,2845,63],{"class":42},[32,2847,96],{"class":83},[32,2849,708],{"class":42},[32,2851,102],{"class":83},[32,2853,2854,2856,2858,2860,2862,2864],{"class":34,"line":46},[32,2855,729],{"class":83},[32,2857,111],{"class":38},[32,2859,734],{"class":42},[32,2861,175],{"class":83},[32,2863,739],{"class":59},[32,2865,605],{"class":83},[32,2867,2868,2870,2872,2874,2876,2878,2880,2882,2884],{"class":34,"line":53},[32,2869,325],{"class":38},[32,2871,787],{"class":83},[32,2873,111],{"class":38},[32,2875,792],{"class":184},[32,2877,795],{"class":83},[32,2879,798],{"class":38},[32,2881,801],{"class":83},[32,2883,804],{"class":38},[32,2885,120],{"class":83},[32,2887,2888,2891,2893],{"class":34,"line":69},[32,2889,2890],{"class":42},"        Validate",[32,2892,819],{"class":83},[32,2894,2895],{"class":150},"\u002F\u002F result discarded\n",[32,2897,2898],{"class":34,"line":74},[32,2899,314],{"class":83},[32,2901,2902],{"class":34,"line":105},[32,2903,605],{"class":83},[2905,2906,2907],"p",{},"What's wrong with this benchmark?",[2909,2910,2911,2915,2922,2925,3064],"details",{},[2912,2913,2914],"summary",{},"Answer",[2905,2916,2917,2918,2921],{},"The compiler may optimize away the ",[29,2919,2920],{},"Validate(user)"," call because the result is discarded (unused). The benchmark may show 0 ns\u002Fop — the function was never called.",[2905,2923,2924],{},"The fix — use a package-level sink to prevent the compiler from eliminating the call:",[23,2926,2928],{"className":25,"code":2927,"language":21,"meta":27,"style":27},"var sink error  \u002F\u002F package-level\n\nfunc BenchmarkGood(b *testing.B) {\n    b.ReportAllocs()\n    b.ResetTimer()\n    user := User{Email: \"test@example.com\"}\n    var err error\n    for i := 0; i \u003C b.N; i++ {\n        err = Validate(user)  \u002F\u002F assign to a variable\n    }\n    sink = err  \u002F\u002F prevent compiler from optimizing away the loop\n}\n",[29,2929,2930,2944,2948,2969,2977,2985,2999,3009,3029,3043,3047,3060],{"__ignoreMap":27},[32,2931,2932,2935,2938,2941],{"class":34,"line":35},[32,2933,2934],{"class":38},"var",[32,2936,2937],{"class":83}," sink ",[32,2939,2940],{"class":38},"error",[32,2942,2943],{"class":150},"  \u002F\u002F package-level\n",[32,2945,2946],{"class":34,"line":46},[32,2947,50],{"emptyLinePlaceholder":49},[32,2949,2950,2952,2955,2957,2959,2961,2963,2965,2967],{"class":34,"line":53},[32,2951,77],{"class":38},[32,2953,2954],{"class":42}," BenchmarkGood",[32,2956,84],{"class":83},[32,2958,699],{"class":87},[32,2960,91],{"class":38},[32,2962,63],{"class":42},[32,2964,96],{"class":83},[32,2966,708],{"class":42},[32,2968,102],{"class":83},[32,2970,2971,2973,2975],{"class":34,"line":69},[32,2972,755],{"class":83},[32,2974,758],{"class":42},[32,2976,1592],{"class":83},[32,2978,2979,2981,2983],{"class":34,"line":74},[32,2980,755],{"class":83},[32,2982,770],{"class":42},[32,2984,1592],{"class":83},[32,2986,2987,2989,2991,2993,2995,2997],{"class":34,"line":105},[32,2988,729],{"class":83},[32,2990,111],{"class":38},[32,2992,734],{"class":42},[32,2994,175],{"class":83},[32,2996,739],{"class":59},[32,2998,605],{"class":83},[32,3000,3001,3004,3006],{"class":34,"line":123},[32,3002,3003],{"class":38},"    var",[32,3005,424],{"class":83},[32,3007,3008],{"class":38},"error\n",[32,3010,3011,3013,3015,3017,3019,3021,3023,3025,3027],{"class":34,"line":132},[32,3012,325],{"class":38},[32,3014,787],{"class":83},[32,3016,111],{"class":38},[32,3018,792],{"class":184},[32,3020,795],{"class":83},[32,3022,798],{"class":38},[32,3024,801],{"class":83},[32,3026,804],{"class":38},[32,3028,120],{"class":83},[32,3030,3031,3034,3036,3038,3040],{"class":34,"line":141},[32,3032,3033],{"class":83},"        err ",[32,3035,814],{"class":38},[32,3037,395],{"class":42},[32,3039,819],{"class":83},[32,3041,3042],{"class":150},"\u002F\u002F assign to a variable\n",[32,3044,3045],{"class":34,"line":154},[32,3046,314],{"class":83},[32,3048,3049,3052,3054,3057],{"class":34,"line":160},[32,3050,3051],{"class":83},"    sink ",[32,3053,814],{"class":38},[32,3055,3056],{"class":83}," err  ",[32,3058,3059],{"class":150},"\u002F\u002F prevent compiler from optimizing away the loop\n",[32,3061,3062],{"class":34,"line":197},[32,3063,605],{"class":83},[2905,3065,3066,2634,3068,3070],{},[29,3067,2650],{},[29,3069,2654],{}," are also missing — without them, you don't see allocation counts, and setup time is included in the benchmark.",[14,3072,3074],{"id":3073},"whats-next","📚 What's Next",[2905,3076,3077,3078,3083],{},"→ ",[3079,3080,3082],"a",{"href":3081},"\u002Fgo\u002F26-concurrency-patterns","26 — Concurrency Patterns"," — worker pool, pipeline, fan-out\u002Ffan-in, generator, and graceful shutdown patterns.",[3085,3086,3087],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}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 .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":27,"searchDepth":46,"depth":46,"links":3089},[3090,3091,3092],{"id":16,"depth":46,"text":17},{"id":655,"depth":46,"text":656},{"id":3073,"depth":46,"text":3074},"Table-driven tests, subtests with t.Parallel, benchmark allocation analysis, fuzzing, httptest, coverage gating, and testify assertions.","md",{},"\u002Fgo\u002F25-testing-and-benchmarking",{"title":5,"description":3093},"go\u002F25-testing-and-benchmarking","CSTVuqxTDW9JryMXoCD4dBQt-nM2eiA1fXxUxd2PEVA",1789924648771]