单元测试

2026年9月6日

标准库自带 testinggo test。测试文件和源码放同一包,名字以 _test.go 结尾。测试函数以 Test 开头,签名是 func TestXxx(t *testing.T)


单元测试

calc.go

package calc

func Add(a, b int) int { return a + b }
go

calc_test.go

package calc

import "testing"

func TestAdd(t *testing.T) {
	got := Add(1, 2)
	if got != 3 {
		t.Fatalf("Add(1,2)=%d, want 3", got)
	}
}
go

命令:

go test
go test -v          # 每个用例的结果
go test -run TestAdd
go test -count=1    # 关掉结果缓存
bash

常用日志:

方法作用
t.Log / t.Logf失败或 -v 时才看见
t.Error / t.Errorf记失败,继续跑这个函数
t.Fatal / t.Fatalf记失败并立刻结束这个测试函数
t.Skip跳过

编辑器里函数左边的绿色三角,等价于 go test -run 那一个。

性能测试是 BenchmarkXxx(b *testing.B),用 go test -bench=.


子测试

同一函数多组输入,用 t.Run。子测试里的 Fatal 只结束这一组,不会停掉整个 TestXxx

func TestAdd(t *testing.T) {
	t.Run("pos", func(t *testing.T) {
		if Add(1, 2) != 3 {
			t.Fatal(Add(1, 2))
		}
	})
	t.Run("neg", func(t *testing.T) {
		if Add(-1, -2) != -3 {
			t.Fatal(Add(-1, -2))
		}
	})
}
go

用例多了就做成表:

cases := []struct {
	name string
	a, b, want int
}{
	{"pos", 1, 2, 3},
	{"neg", -1, -2, -3},
	{"zero", 0, 0, 0},
}
for _, c := range cases {
	t.Run(c.name, func(t *testing.T) {
		if got := Add(c.a, c.b); got != c.want {
			t.Fatalf("got %d want %d", got, c.want)
		}
	})
}
go

go test -run TestAdd/pos 只跑其中一个。


TestMain 函数

一个包可以有一个 TestMain,当作测试进程的入口,做准备和收尾:

func TestMain(m *testing.M) {
	// 建临时库、改环境变量……
	code := m.Run()
	// 清理
	os.Exit(code)
}
go

一定要调用 m.Run(),并且用它的返回码 os.Exit。漏了 m.Run(),所有测试都不会跑。


参考文档