Go测试不推荐用assert库,应使用t.Error、t.Errorf、t.Fatal等原生方法;基础类型用==/!=比较,复杂类型用reflect.DeepEqual;t.Errorf适合批量校验,t.Fatal用于前置失败后终止执行。

Go 测试里没有 assert,别写 assert.Equal
Go 标准测试库 testing.T 不提供断言函数,强行引入第三方 assert 库(比如 github.com/stretchr/testify/assert)会让错误堆栈指向断言内部,掩盖真实失败位置。官方推荐用 t.Error、t.Errorf、t.Fatal 等原生方法显式判断 + 报错。
t.Errorf 是最常用的结果比对方式
它输出错误信息但不中断执行,适合批量校验多个字段或条件。常见写法是先计算预期值和实际值,再用 != 或 reflect.DeepEqual 判断,出错时把两值都打印出来便于调试:
func TestAdd(t *testing.T) {
got := Add(2, 3)
want := 5
if got != want {
t.Errorf("Add(2, 3) = %d, want %d", got, want)
}
}
- 数值、字符串、布尔等基础类型直接用
==或!= - 结构体、切片、map 等复杂类型必须用
reflect.DeepEqual(got, want),不能用== - 避免在
t.Errorf中调用可能 panic 的函数(如json.Marshal),否则测试会崩溃而非报错
用 t.Fatal 终止后续逻辑的场景
当某个前置检查失败后,后续断言已无意义(比如初始化失败、依赖服务不可用),就该用 t.Fatal 或 t.Fatalf 立即退出:
func TestParseConfig(t *testing.T) {
cfg, err := LoadConfig("test.yaml")
if err != nil {
t.Fatalf("LoadConfig failed: %v", err) // 后续所有 cfg 字段检查都不必执行了
}
if cfg.Timeout <= 0 {
t.Error("Timeout must be positive")
}
}
-
t.Fatal和t.Error都会标记测试为失败,区别只在是否继续执行 - 不要用
log.Fatal或os.Exit,它们会跳过测试框架的清理逻辑 - 并发测试中慎用
t.Fatal,它只终止当前 goroutine 的测试逻辑,不影响其他 goroutine
表驱动测试 + 错误信息结构化更易定位问题
多个输入输出组合时,用切片定义测试用例,每个 case 包含 name、input、want,并用 t.Run 分组运行。失败时能一眼看到是哪个 case 崩了:
立即学习“go语言免费学习笔记(深入)”;
func TestSplit(t *testing.T) {
tests := []struct {
name string
input string
sep string
want []string
}{
{"empty", "", ",", []string{}},
{"single", "a", ",", []string{"a"}},
{"multi", "a,b,c", ",", []string{"a", "b", "c"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := strings.Split(tt.input, tt.sep)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Split(%q, %q) = %v, want %v", tt.input, tt.sep, got, tt.want)
}
})
}
}
- 每个
t.Run子测试独立计时、独立失败/成功状态 -
t.Errorf中把输入参数也打出来,避免只看输出无法还原上下文 - 复杂结构体比较失败时,可加
fmt.Sprintf("%+v", x)打印字段名,而不是只打%v
got 和 want 都出现在 t.Errorf 的格式串里,并且复杂值走 reflect.DeepEqual,基本不会卡在“知道错了但看不出哪错”。










