go test能运行不代表测试真正执行;需确认_test.go命名、testxxx函数格式、在被测目录运行go test -v验证,再用go tool cover -func定位未覆盖分支并显式构造error/context等边界场景。

go test 能跑起来,不代表测试真在执行——常见情况是 go test -cover 显示 coverage: 0.0% of statements,本质是测试压根没被识别或没进到函数体里。
为什么 go test -cover 总是 0.0%
这不是覆盖率低,是测试根本没触发。先确认三件事:
-
_test.go文件名必须严格以_test.go结尾(handler_test.go✅,test_handler.go❌) - 测试函数必须是
func TestXxx(t *testing.T)格式(func checkLogin()或func Testlogin()都不会被识别) - 命令要运行在被测代码所在目录下(比如代码在
internal/auth/,就得进该目录再跑go test -v;在项目根目录只跑go test -cover默认不递归子包)
最简单验证方式:先执行 go test -v,看到输出里有 === RUN TestXXX 才算真正开始跑测试。
用 go tool cover -func 定位真实未覆盖函数
总覆盖率数字毫无诊断价值。真正要盯的是具体哪些函数卡在 0.0%、33.3% 或 50.0%,它们往往暴露关键盲区:
-
if err != nil { return }分支一次没进过 → mock 总返回nil错误,没构造失败路径 -
else块写在if len(s) == 0后 → 测试只传了非空切片,nil切片和空切片都没覆盖 -
defer resp.Body.Close()没执行 → handler 提前return,清理逻辑静默跳过 - 带
context.Context的函数没传已cancel()的 ctx → 超时/取消分支永远沉默
执行这两步:go test -covermode=count -coverprofile=coverage.out .go tool cover -func=coverage.out
显式构造 error 和 context 分支,别等“自然失败”
Go 的错误处理是显式的,但多数测试只走 happy path。线上出问题的,恰恰是那些没被验证的 if err != nil 分支:
- 对每个返回
error的外部调用,mock 至少定义两条行为:mock.On("Get").Return(user, nil)和mock.On("Get").Return(nil, errors.New("timeout")) - HTTP handler 测试中:
httptest.NewRequest("POST", "/login", nil)触发json.Decode失败;bytes.NewReader([]byte(""))模拟空 JSON body - context 分支用表格驱动:
{"cancelled", func() context.Context { ctx, cancel := context.WithCancel(context.Background()); cancel(); return ctx }(), true}
拆函数 + 依赖注入,让不可控逻辑可测
直接在函数里调 time.Now()、rand.Intn() 或全局 *sql.DB,会导致该函数几乎无法写出稳定、快速的单元测试:
- 把时间/随机逻辑抽成函数类型参数,例如
NowFunc func() time.Time,测试时传入固定值 - DB 或 HTTP 客户端通过结构体字段注入,而不是在函数内硬编码
db.QueryRow - 避免在测试里启动 goroutine 后用
time.Sleep等结果;改用sync.WaitGroup或chan struct{}显式同步
真正难测的从来不是语法,而是那些隐式依赖和提前退出路径——它们藏在 defer 里、else 里、ctx.Err() 判断里,不手动构造就永远看不见。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











