第一个 Go TDD 测试需新建以 _test.go 结尾的文件,定义 func TestXxx(t *testing.T) 函数,先写失败断言(如 t.Errorf),确认 go test -v 显示 === RUN TestXxx 和明确错误,再实现函数使其通过。

怎么写第一个 Go TDD 测试(go test 报错“no test files”)
Go 的测试文件必须以 _test.go 结尾,且函数名必须是 TestXxx(首字母大写、前缀 Test),否则 go test 直接忽略。常见错误是把测试写在普通 .go 文件里,或函数名写成 testAdd、Test_add —— 这两种都会静默跳过。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 新建
calculator_test.go,和calculator.go放同一目录 - 测试函数签名必须是
func TestAdd(t *testing.T),t类型不能错 - 先写测试再写实现:比如先写断言
got := Add(2, 3); want := 5; if got != want { t.Errorf("got %d, want %d", got, want) } - 此时运行
go test会报undefined: Add—— 这是红阶段正常现象,别急着实现,先确认测试能失败
为什么 go test -v 显示 “FAIL” 却没输出错误详情
因为 t.Error 和 t.Fatal 默认只在测试失败时打印日志,但如果你的测试逻辑没走到断言分支(比如条件判断写反了),或者用了 t.Log(它不触发失败),就会看起来“没报错却失败”。更隐蔽的是:如果函数 panic 但没被 recover,go test 会中断并只显示 panic 栈,不显示你写的 t.Error。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 红阶段务必看到明确失败信息:手动让被测函数返回错误值,或用
t.Fatal("forced fail")验证测试框架是否真在跑 - 避免在测试中用
log.Fatal或直接panic,改用t.Fatal—— 它会终止当前测试但不影响其他测试 - 检查
go test -v输出里是否有=== RUN TestAdd行;没有说明测试函数根本没被识别
重构阶段怎么安全地改代码而不破坏测试(go test 通过但行为变了)
Go 没有运行时反射式 mock,TDD 重构常卡在“依赖难替换”。比如原函数调用了 http.Get 或 os.ReadFile,硬编码会导致测试慢、不稳定、无法覆盖边界。这时候不是靠删代码来“重构”,而是靠抽离接口 + 依赖注入。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 把外部调用包进一个接口,比如定义
type Reader interface { Read(path string) ([]byte, error) } - 原函数接收该接口为参数(或通过 struct 字段注入),测试时传入内存实现(如
memReader{content: "ok"}) - 不要在重构阶段新增功能:比如测试只覆盖
Add(2,3),就别顺手加AddAll;保持每次只动一丁点,再跑go test确认绿灯 - 注意导出规则:接口方法名必须大写(
Read),否则测试文件无法实现它
测试覆盖率高 ≠ TDD 做得对(go test -cover 95% 但红绿流程形同虚设)
很多人跑通 go test -cover 就以为 TDD 完成了,但 TDD 的核心是节奏:先写失败测试 → 写最简实现让它通过 → 再清理代码。如果跳过红阶段(比如先写好函数再补测试),或一次写一堆测试再一起跑,就失去了 TDD 对设计的即时反馈价值。
容易被忽略的点:
- 测试命名要反映意图,不是
TestAdd而是TestAdd_ReturnsSumOfTwoPositiveNumbers(Go 社区接受长名,便于快速定位场景) - 每个测试只验证一件事:不要在一个
TestAdd里测正数、负数、零、溢出 —— 拆成多个函数,失败时一眼知道哪条路径崩了 -
go test默认不运行示例函数(ExampleXxx)或基准测试(BenchmarkXxx),别误把它们当 TDD 环节
红绿重构不是三步仪式,是写每行业务代码前,手指在测试文件和实现文件之间来回切换的肌肉记忆。一旦开始写 if 就该问:这个分支,我有没有对应的 t.Run 子测试?











