go测试文件必须与被测代码同目录同包,按功能拆分_test.go文件,集成测试单独放integration/目录并用-tags integration运行,优先使用带明确name字段的表驱动测试。

测试文件必须和被测代码在同一个包里
Go 的测试机制要求 xxx_test.go 文件和它要测试的源码(比如 service.go)处于同一目录、同名包下。不能把测试放到 test/ 子目录或单独建 test 包——那样会变成另一个包,无法访问未导出的函数、字段或变量,导致大量测试写不出来或只能暴露内部实现。
常见错误现象:undefined: someInternalFunc 或测试只能测 public 接口,覆盖不到关键逻辑分支。
- 正确做法:所有
_test.go文件和对应源码放同一目录,package声明完全一致(如都是package service) - 如果想测私有行为,就别挪走;如果真需要隔离(比如集成测试),用
package main+main_test.go单独组织,但那是另一类场景 - 不要加
//go:build integration到单元测试文件里——这会让go test默认跳过,除非显式传-tags integration
按功能分组,避免 _test.go 文件爆炸
一个 handler_test.go 里塞 20 个 TestXXX 函数,很快变得难定位、难维护。更合理的方式是按被测对象职责拆分测试文件,命名体现范围:
-
user_service_test.go:专注测试UserService的核心逻辑 -
user_repository_test.go:只测数据层接口与 mock 实现 -
user_handler_test.go:HTTP handler 的路由、参数绑定、状态码等 - 若某模块逻辑复杂(如含多种策略),可进一步拆为
user_service_validation_test.go、user_service_persistence_test.go
注意:拆分依据是「被测主体」而非「测试类型」。不用单独建 unit/ 或 mock/ 目录——Go 不鼓励这种分层,反而增加 import 路径和包管理负担。
立即学习“go语言免费学习笔记(深入)”;
集成测试用单独目录 + main 包,和单元测试物理隔离
真正需要启动数据库、调用外部 API 或跑完整 HTTP 流程的测试,不适合和单元测试混在一起。它们启动慢、依赖多、不稳定,容易拖垮 CI 速度,也违反单元测试快速反馈原则。
- 新建
integration/目录(与cmd/、internal/平级),里面放main.go和main_test.go,package main - 在
main_test.go中用os.Setenv控制测试环境(如指向本地 test DB),避免污染主流程配置 - 运行时加 tag:
go test -tags integration ./integration,CI 中可单独调度 - 不推荐用
testutil包共享 setup 逻辑——跨包引入易引发循环依赖;改用函数闭包或每个测试自己初始化更可控
表驱动测试优先,但别为了表格牺牲可读性
Go 社区推崇表驱动测试(table-driven tests),确实能减少重复代码,但硬套模板可能让失败信息变模糊、调试变困难。
- 适合场景:输入/输出结构一致、边界值明确(如 JSON 解析、字符串 trim、状态机转换)
- 关键点:每个
testcase的name字段必须具体,比如"empty_body_returns_400"而不是"case1" - 避免嵌套过深:不要在 table 里塞 closure 或复杂 struct 初始化;setup 步骤多的,宁可单写一个
TestXXX函数 - 错误时打印上下文:
t.Errorf("for %s: expected %v, got %v", tc.name, tc.want, got)—— 这比默认报错信息有用得多
真正容易被忽略的是:很多人把 go:generate 或 mock 工具生成的测试骨架当成品,结果 case 缺失、断言松散、没覆盖 error path。测试质量不在数量,在是否真实模拟调用方视角。










