Go测试函数必须命名为TestXxx且参数为*testing.T,定义在_test.go文件中、同包名下;需用t.Error/Fatal报告失败,推荐表驱动测试。

Go测试函数必须叫TestXxx,且参数固定为*testing.T
Go的测试函数不是随便命名的,必须以Test开头,后接**大写字母开头的驼峰名**(如TestAdd、TestParseURL),不能是test_add或testAdd。函数签名也严格限定为func(t *testing.T)——少一个星号、换类型、加参数都会导致go test直接忽略该函数。
常见错误现象:go test运行后显示no tests to run,但文件里明明写了函数——八成是命名不合规或参数不对。
- 函数必须定义在
_test.go结尾的文件中(如math_test.go) - 函数必须是包级导出函数(首字母大写),但名字本身不需导出,
TestXxx已满足导出要求 - 不能用
*testing.B代替*testing.T,那是基准测试用的
用t.Errorf和t.Fatal报告失败,别用panic或log
测试中检查逻辑失败时,必须调用t.Error*系列方法,比如t.Errorf("expected %v, got %v", want, got)。这些方法会标记当前测试失败,但允许继续执行后续断言;而t.Fatal*会立即终止当前测试函数。
绝对不要在测试里写panic、log.Fatal或os.Exit——它们会让go test无法正确统计失败数,甚至中断整个测试套件。
-
t.Error:记录错误,继续执行 -
t.Fatal:记录错误,立刻返回(适合前置条件不满足,如文件不存在) -
t.Log:只输出日志,不影响测试结果 - 所有
t.*方法只能在测试函数内调用,不能传给子函数再调(除非显式传*testing.T)
表驱动测试是Go推荐写法,用struct切片组织用例
Go标准库和主流项目几乎都用表驱动(table-driven)方式写测试,把输入、期望输出、说明打包进一个结构体切片,然后range循环执行。这样比重复写一堆TestXxx1/TestXxx2更易维护、覆盖更全。
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 := Split(tt.input, tt.sep)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Split() = %v, want %v", got, tt.want)
}
})
}
}
注意:t.Run创建子测试,让每个用例独立显示、可单独运行(如go test -run=TestSplit/empty),还能避免变量闭包陷阱(循环中tt被复用)。
测试文件路径和包名要和被测代码对齐
测试文件必须和被测代码在**同一目录**,且声明相同的包名(通常是package xxx,不是package xxx_test)。只有想测试包私有符号(如未导出函数、变量)时,才需要另建xxx_internal_test.go并用package xxx_test——但这属于高级用法,日常80%场景不需要。
- 错误做法:把
http_test.go放在test/子目录下 →go test找不到 - 错误做法:测试文件写
package main→ 编译失败 - 如果被测代码在
cmd/mytool/下,测试文件也得放那儿,不能挪到根目录
路径和包名错一个字符,测试就静默失效——这是最常被忽略的硬性约束。










