Go单元测试需写Test开头、*testing.T参数的函数,用t.Run组织子测试、t.Errorf报告失败,配合go test运行;测试文件名以_test.go结尾且与被测代码同包。

在 Go 中用 testing 包写单元测试,核心是写以 Test 开头、参数为 *testing.T 的函数,然后用 t.Run 组织子测试、t.Errorf 报告失败,配合 go test 运行验证逻辑是否符合预期。
基础测试结构:从一个简单函数开始
假设你有一个计算两个整数最大值的函数:
func Max(a, b int) int {
if a > b {
return a
}
return b
}
对应测试文件(如 max_test.go)中这样写:
func TestMax(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"positive", 3, 5, 5},
{"equal", 4, 4, 4},
{"negative", -2, -7, -2},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Max(tt.a, tt.b)
if got != tt.want {
t.Errorf("Max(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want)
}
})
}
}
- 测试文件名必须以
_test.go结尾,且与被测代码在同一包内 -
t.Run支持命名子测试,便于定位失败用例,也支持并行(加t.Parallel()) - 用结构体切片组织多组输入/期望输出,避免重复代码,提升可读性和覆盖度
验证边界与错误路径:不只是“正常情况”
真实逻辑常涉及空值、零值、错误返回等。比如一个解析 JSON 字符串的函数:
立即学习“go语言免费学习笔记(深入)”;
func ParseUser(s string) (*User, error) {
var u User
if err := json.Unmarshal([]byte(s), &u); err != nil {
return nil, fmt.Errorf("parse user: %w", err)
}
return &u, nil
}
测试要覆盖成功、空字符串、非法 JSON、字段缺失等情况:
- 成功解析:检查字段值是否正确赋值
- 空输入:
ParseUser("")应返回非 nil error - 非法 JSON:
ParseUser("{invalid")同样应报错,且错误信息包含预期前缀 - 用
if err != nil+t.Error或assert.ErrorContains(需引入github.com/stretchr/testify)判断错误内容
模拟依赖与控制副作用
如果函数依赖外部调用(如 HTTP 请求、数据库查询),不能在单元测试中真实发起网络请求。推荐做法是抽象接口、注入依赖:
type Fetcher interface {
Get(url string) ([]byte, error)
}
func DownloadAndParse(f Fetcher, url string) (string, error) {
data, err := f.Get(url)
if err != nil {
return "", err
}
return strings.TrimSpace(string(data)), nil
}
测试时传入一个内存实现:
type mockFetcher struct {
data []byte
err error
}
func (m mockFetcher) Get(_ string) ([]byte, error) {
return m.data, m.err
}
func TestDownloadAndParse(t *testing.T) {
t.Run("success", func(t *testing.T) {
got, err := DownloadAndParse(mockFetcher{data: []byte(" hello ")}, "http://x")
if err != nil || got != "hello" {
t.Errorf("unexpected result: %v, %v", got, err)
}
})
t.Run("fetch error", func(t *testing.T) {
_, err := DownloadAndParse(mockFetcher{err: errors.New("timeout")}, "http://x")
if err == nil {
t.Error("expected error, got nil")
}
})
}
- 不直接调用
http.Get,而是通过接口接收行为,测试更可控、更快、无副作用 - mock 实现只需满足接口,无需完整逻辑,聚焦验证被测函数对依赖返回的处理是否正确
运行与调试技巧
使用标准命令快速验证:
-
go test:运行当前目录所有测试 -
go test -v:显示每个测试名称和日志(t.Log输出可见) -
go test -run=^TestMax$:只运行指定测试函数 -
go test -cover:查看测试覆盖率(建议配合-coverprofile=c.out && go tool cover -html=c.out生成可视化报告) - 在测试中用
t.Fatal或t.Fatalf遇错终止当前子测试,避免后续断言干扰判断










