示例说明
表驱动是 Go 测试的惯用形态:把用例收敛成结构体切片,断言逻辑只写一遍——新增用例只是加一行数据,不是复制一段测试函数。每个用例命名成子测试后可以单独 go test -run 精准重跑,失败信息自动带上用例名。t.Parallel 让子测试并发执行,大套件耗时显著缩短。
技术要点
- 用例即数据,断言逻辑单份维护
- 子测试命名支持精准选择执行
- 错误分支与结果分支双断言
代码解读
测试表覆盖七条路径:轻重两档基础价、快递翻倍、超重加价、金牌折扣与两条错误边界。子测试先判 wantErr——期望出错的用例核对错误消息文本;正常用例核对金额。Fatalf 与 Errorf 的选择遵循失败即停与继续收集的差异。循环内捕获变量保证并行安全。
使用建议
错误消息比对用精确匹配时要同步维护文案,也可只断言错误出现。期望值手工计算易错,关键公式用属性测试交叉验证。
代码示例
package main
import (
"errors"
"testing"
)
var errNotFound = errors.New("order not found")
func ShippingCost(weightKg int, express bool, tier string) (int, error) {
if weightKg <= 0 {
return 0, errors.New("weight must be positive")
}
if weightKg > 30 {
return 0, errors.New("weight exceeds courier limit")
}
base := 8
if weightKg > 10 {
base += (weightKg - 10) * 2
}
if express {
base *= 2
}
if tier == "gold" {
base -= 4
}
return base, nil
}
func TestShippingCost(t *testing.T) {
t.Parallel()
tests := []struct {
name string
weight int
express bool
tier string
want int
wantErr error
}{
{name: "light standard", weight: 2, express: false, tier: "", want: 8},
{name: "light express", weight: 2, express: true, tier: "", want: 16},
{name: "heavy adds per kg", weight: 15, express: false, tier: "", want: 18},
{name: "heavy express gold", weight: 15, express: true, tier: "gold", want: 32},
{name: "gold floor at zero", weight: 1, express: false, tier: "gold", want: 4},
{name: "zero weight rejected", weight: 0, express: false, tier: "", wantErr: errors.New("weight must be positive")},
{name: "over courier limit", weight: 31, express: false, tier: "", wantErr: errors.New("weight exceeds courier limit")},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := ShippingCost(tt.weight, tt.express, tt.tier)
if tt.wantErr != nil {
if err == nil || err.Error() != tt.wantErr.Error() {
t.Fatalf("ShippingCost(%d, %v, %q) error = %v, want %v",
tt.weight, tt.express, tt.tier, err, tt.wantErr)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != tt.want {
t.Errorf("ShippingCost(%d, %v, %q) = %d, want %d",
tt.weight, tt.express, tt.tier, got, tt.want)
}
})
}
}