Day 27|测试体系与测试驱动思维(testing / mock / 表驱动测试)[Go 语言 30 天系统学习计划]

学习目标

  • 理解 Go 官方 testing 包的设计哲学
  • 掌握表驱动测试的标准写法
  • 学会通过接口进行 mock 设计
  • 建立“测试是工程资产”的认知

一、为什么 Go 如此重视测试

在 Go 的工程文化中:

  • 测试是语言级支持的一部分
  • 不是第三方框架附加能力

Go 官方提供:

  • testing:单元测试
  • testing/quick:属性测试
  • go test:统一测试入口

工程认知:可测试性是代码质量的直接体现。


二、testing 包的基本结构

一个最基本的测试:

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

基本规则:

  • 文件名以 _test.go 结尾
  • 测试函数以 Test 开头
  • 参数必须是 *testing.T

三、表驱动测试(Table-Driven Test)

Go 社区最推荐的测试方式。

示例:

func TestAdd_Table(t *testing.T) {
    tests := []struct {
        name   string
        a, b   int
        expect int
    }{
        {"both positive", 1, 2, 3},
        {"with zero", 0, 5, 5},
        {"negative", -1, 1, 0},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := Add(tt.a, tt.b); got != tt.expect {
                t.Fatalf("expect %d, got %d", tt.expect, got)
            }
        })
    }
}

工程价值:

  • 减少重复代码
  • 测试用例结构清晰
  • 便于新增测试场景

四、子测试(t.Run)的意义

t.Run 的作用:

  • 将一个测试拆成多个子场景
  • 失败定位更精确

配合表驱动测试,是 Go 测试的标准形态。

工程结论:一个 Test 函数,代表一个“行为”;子测试代表“场景”。


五、测试中的失败策略

常用方法:

  • t.Errorf:记录错误,继续执行
  • t.Fatalf:立即失败,终止当前测试

工程建议:

  • 前置条件失败,用 Fatal
  • 结果校验失败,用 Errorf 或 Fatalf

六、可测试性设计:接口优先

测试困难,通常不是 testing 的问题,而是设计问题。

示例:

type Store interface {
    Get(id int) (Item, error)
}

业务逻辑只依赖接口:

func Service(s Store) error {
    _, err := s.Get(1)
    return err
}

工程价值:

  • 可以轻松 mock
  • 无需真实 DB / RPC

七、mock 的工程策略

Go 社区更偏好:

  • 手写 mock
  • 而不是重度依赖 mock 框架

简单 mock 示例:

type mockStore struct {
    err error
}

func (m *mockStore) Get(id int) (Item, error) {
    return Item{}, m.err
}

工程结论:

mock 是设计自然产生的副产品,而不是强行引入的工具。


八、测试中的并发注意事项

测试中使用 goroutine 时:

  • 要特别注意数据竞争
  • 测试本身也会并发执行

推荐:

  • 配合 go test -race
  • 避免共享可变全局状态

九、单元测试 vs 集成测试

单元测试:

  • 关注函数 / 模块行为
  • 速度快、定位准

集成测试:

  • 关注模块协作
  • 验证系统边界

工程建议:

单元测试为主,集成测试兜底。


十、测试驱动的工程价值

  • 测试迫使你写出可组合的代码
  • 测试是最好的文档
  • 测试让重构更安全

工程结论:测试不是成本,是长期收益。


十一、测试体系总结

  • testing 包简单但强大
  • 表驱动测试是标准实践
  • 接口设计决定测试难度

一句话总结:

写不好测试,往往是设计已经出了问题。


预告:Day 28|构建、发布与 CI/CD 基础

下一天将系统讲解:

  • Go 构建流程与交叉编译
  • 版本号与构建信息注入
  • CI 中的 go test / go build
  • 工程级发布流程

发表评论