Go 单元测试实战:table-driven 用例、testify 断言与接口 Mock
test12026-09-241 次阅读
为什么用 table-driven 测试
Go 社区习惯把一组相似用例写进一张"表",循环跑同一个被测函数,新增用例只加一行结构体,不必复制粘贴测试函数。相比 JUnit 式的一个用例一个函数,table-driven 更紧凑,也更容易覆盖边界。
被测函数与接口
先定义一个依赖外部存储的折扣服务,故意把存储抽象成接口,这样测试时可以用内存实现替换,不必连真实数据库。
package discount
import "errors"
type Store interface {
GetRule(id string) (int, error)
}
type Service struct{ store Store }
func (s *Service) Calc(id string, price int) (int, error) {
off, err := s.store.GetRule(id)
if err != nil {
return 0, err
}
if off < 0 || off > 100 {
return 0, errors.New("折扣越界")
}
return price * (100 - off) / 100, nil
}
用 testify 写断言与 Mock
testify/assert 提供可读的断言;mock 包可以声明"期望被调用几次、返回什么",既验证结果也验证调用行为。下面是一个内存 mock 和一张用例表。
package discount
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
type fakeStore struct{ mock.Mock }
func (m *fakeStore) GetRule(id string) (int, error) {
args := m.Called(id)
return args.Int(0), args.Error(1)
}
func TestCalc(t *testing.T) {
cases := []struct {
name string
rule int
ruleErr error
price int
want int
wantErr bool
}{
{"正常九折", 10, nil, 100, 90, false},
{"满减零折", 0, nil, 50, 50, false},
{"存储报错", 0, errors.New("db down"), 50, 0, true},
{"折扣越界", 200, nil, 50, 0, true},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := new(fakeStore)
m.On("GetRule", "r1").Return(c.rule, c.ruleErr)
svc := &Service{store: m}
got, err := svc.Calc("r1", c.price)
if c.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, c.want, got)
m.AssertExpectations(t)
})
}
}
覆盖子测试与基准
t.Run 把每个用例变成独立子测试,失败时只显示对应 name;配合 -run 'TestCalc/正常九折' 可单独跑某条。需要关注性能时再加 Benchmark,用 b.ResetTimer() 排除预热。
func BenchmarkCalc(b *testing.B) {
m := new(fakeStore)
m.On("GetRule", "r1").Return(10, nil)
svc := &Service{store: m}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = svc.Calc("r1", 100)
}
}
实践要点
- 把外部依赖(DB、HTTP、时间)抽象成接口,测试用 mock 或内存实现替换,让单测稳定可重复。
- 边界用例(零值、越界、错误返回)和正常用例同等重要,table-driven 让它们成本一致。
- go test -cover 看覆盖率,-race 在并发代码下必开,能抓出数据竞争。
T
test1
文章作者
为什么用 table-driven 测试 Go 社区习惯把一组相似用例写进一张"表",循环跑同一个被测函数,新增用例只...
- 分类
- 技术
- 发布时间
- 2026-09-24
- 字数
- 约 2330 字
- 阅读
- 1 次
