Golang初级项目如何编写单元测试

12次阅读

go测试文件须命名为_test.go且与源文件同目录同包;测试函数以Test开头并接收testing.T;用t.Error/t.Fatal断言,推荐表驱动测试和接口mock。

Golang初级项目如何编写单元测试

测试文件命名和位置怎么放才不会被忽略

Go 的 go test 命令只识别以 _test.go 结尾的文件,且必须和被测代码在同一个包(即同目录下)。如果放在 test/ 子目录或命名为 mytest.gogo test 直接跳过——不是报错,是静默忽略。

  • 正确命名:calculator_test.go(对应 calculator.go
  • 必须同包:不能加 package test,得写 package mainpackage calculator,和源文件一致
  • 测试函数必须以 Test 开头,且接收单个 *testing.T 参数,例如 func TestAdd(t *testing.T)

如何用 testing.T 写一个不 panic 的基础断言

Go 标准库不提供 assert.Equal 这类函数,所有判断靠 t.Errort.Fatal 手动触发。关键区别在于:t.Error 记录错误但继续执行,t.Fatal 立即终止当前测试函数。

func TestAdd(t *testing.T) {     result := Add(2, 3)     if result != 5 {         t.Errorf("Add(2, 3) = %d; want 5", result) // 推荐:带上下文     } }
  • 别用 paniclog.Fatal:会绕过 testing 框架,导致覆盖率统计失败、无法并行运行
  • 避免裸比较 if result != 5 { t.Fail() }:没有输出信息,排查时只能看源码猜
  • 字符串插值建议用 %v 而非 %d,适配任意类型

表驱动测试怎么组织才不重复写 t.Run

对多个输入/输出组合,用切片定义测试用例,配合 t.Run 实现子测试隔离。每个子测试独立计时、可单独运行(如 go test -run=TestAdd/2+3),失败时也只报具体子项。

func TestAdd(t *testing.T) {     tests := []struct {         a, b, want int     }{         {2, 3, 5},         {-1, 1, 0},         {0, 0, 0},     }     for _, tt := range tests {         t.Run(fmt.Sprintf("%d+%d", tt.a, tt.b), func(t *testing.T) {             if got := Add(tt.a, tt.b); got != tt.want {                 t.Errorf("got %d, want %d", got, tt.want)             }         })     } }
  • 子测试名必须唯一,否则后一个覆盖前一个;用 fmt.Sprintf 构造比硬编码更安全
  • 循环变量 tt 必须在 for 内部声明(for _, tt := range tests),否则闭包会捕获最后一次迭代的值
  • 不要在子测试里调 t.Parallel() 除非确认函数无共享状态——初级项目通常没必要

mock 外部依赖时为什么不能直接 new 一个结构体

当函数依赖数据库http 客户端等,需抽象为接口再替换实现。直接 new(http.Client) 仍会发起真实请求;而用接口 + mock,才能控制返回值、验证调用次数。

立即学习go语言免费学习笔记(深入)”;

type Fetcher interface {     Get(url string) (string, error) }  func Download(f Fetcher, url string) (string, error) {     return f.Get(url) }  // 测试用 mock type mockFetcher struct{ called int } func (m *mockFetcher) Get(url string) (string, error) {     m.called++     return "fake-body", nil }
  • 接口定义要窄:只包含当前函数实际调用的方法,别把整个 http.Client 方法都塞进去
  • mock 结构体字段(如 called)用于验证行为,比如断言 if m.called != 1 { t.Error("expected call once") }
  • 初级项目慎用第三方 mock 库(如 gomock):手写 mock 更轻量,也更容易理解依赖边界

测试最难的部分不是写断言,而是决定「该测什么」——比如边界值、错误路径、并发场景。很多初级项目只测 happy path,结果上线后遇到空指针或超时就崩,却没对应测试用例。

text=ZqhQzanResources