Go测试文件须命名为_test.go且与源文件同目录同包;测试函数以Test开头并接收testing.T;用t.Error/t.Fatal断言,推荐表驱动测试和接口mock。
Go 的 go test 命令只识别以 _test.go 结尾的文件,且必须和被测代码在同一个包(即同目录下)。如果放在 test/ 子目录或命名为 mytest.go,go test 直接跳过——不是报错,是静默忽略。
calculator_test.go(对应 calculator.go)package test,得写 package main 或 package calculator,和源文件一致Test 开头,且接收单个 *testing.T 参数,例如 func TestAdd(t *testing.T)
Go 标准库不提供 assert.Equal 这类函数,所有判断靠 t.Error、t.Fatal 手动触发。关键区别在于:t.Error 记录错误但继续执行,t.Fatal 立即终止当前测试函数。
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; want 5", result) // 推荐:带上下文
}
}
panic 或 log.Fatal:会绕过 testing 框架,导致覆盖率统计失败、无法并行运行if result != 5 { t.Fail() }:没有输出信息,排查时只能看源码猜%v 而非 %d,适配任意类型对多个输入/输出组合,用切片定义测试用例,配合 t.Run 实现子测试隔离。每个子测试独立计时、可单独运行(如 go test -run=TestAdd/2+3),失败时也只报具体子项。
func TestAdd(t *testing.T) {
tests := []struct {
a, b, want int
}{
{2, 3, 5},
{-1, 1, 0},
{0, 0, 0},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("%d+%d", tt.a, tt.b), func(t *testing.T) {
if got := Add(tt.a, tt.b); got != tt.want {
t.Errorf("got %d, want %d", got, tt.want)
}
})
}
}
fmt.Sprintf 构造比硬编码更安全tt 必须在 for 内部声明(for _, tt := range tests),否则闭包会捕获最后一次迭代的值t.Parallel() 除非确认函数无共享状态——初级项目通常没必要当函数依赖数据库、HTTP 客户端等,需抽象为接口再替换实现。直接 new(http.Client) 仍会发起真实请求;而用接口 + mock,才能控制返回值、验证调用次数。
type Fetcher interface {
Get(url string) (string, error)
}
func Download(f Fetcher, url string) (string, error) {
return f.Get(url)
}
// 测试用 mock
type mockFetcher struct{ called int }
func (m *mockFetcher) Get(url str
ing) (string, error) {
m.called++
return "fake-body", nil
}
http.Client 方法都塞进去called)用于验证行为,比如断言 if m.called != 1 { t.Error("expected call once") }