Go语言测试中的断言怎么写_测试结果判断方式说明

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.Errort.Errorft.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.Fatalt.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.Fatalt.Error 都会标记测试为失败,区别只在是否继续执行
  • 不要用 log.Fatalos.Exit,它们会跳过测试框架的清理逻辑
  • 并发测试中慎用 t.Fatal,它只终止当前 goroutine 的测试逻辑,不影响其他 goroutine

表驱动测试 + 错误信息结构化更易定位问题

多个输入输出组合时,用切片定义测试用例,每个 case 包含 nameinputwant,并用 t.Run 分组运行。失败时能一眼看到是哪个 case 崩了:

f

unc 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
Go 测试里最容易被忽略的是:错误信息里没包含足够上下文,或者用了不安全的 deep-equal 替代方案(比如自己遍历比较 slice)。只要 gotwant 都出现在 t.Errorf 的格式串里,并且复杂值走 reflect.DeepEqual,基本不会卡在“知道错了但看不出哪错”。