题目
实现表格驱动测试并正确处理错误场景
信息
- 类型:问答
- 难度:⭐⭐
考点
表格驱动测试设计,错误处理测试,测试辅助函数使用
快速回答
本题考察表格驱动测试的实现和错误场景处理能力,核心要点包括:
- 使用
[]struct{}定义测试用例表格 - 通过
t.Run()为每个用例创建独立子测试 - 使用
errors.Is/errors.As精确比较错误 - 处理预期错误和正常结果的不同断言逻辑
- 利用
t.Cleanup清理测试资源
原理说明
表格驱动测试是Go测试的核心模式,通过结构体切片定义多组输入/输出,循环执行测试逻辑。关键优势:
- 减少重复代码
- 统一管理测试数据
- 支持动态添加用例
- 子测试隔离保证独立性
错误测试需注意:
- 区分错误类型(sentinel error/自定义error)
- 错误值比较需使用
errors.Is而非== - 验证错误消息的稳定性(避免依赖易变字符串)
代码示例
被测函数:
// 文件:mathutil.go
type DivisionError struct {
Dividend int
Divisor int
}
func (e DivisionError) Error() string {
return fmt.Sprintf("cannot divide %d by %d", e.Dividend, e.Divisor)
}
func SafeDivide(a, b int) (int, error) {
if b == 0 {
return 0, DivisionError{Dividend: a, Divisor: b}
}
return a / b, nil
}测试实现:
// 文件:mathutil_test.go
func TestSafeDivide(t *testing.T) {
tests := []struct {
name string
a, b int
want int
expectError bool
errType error
}{{
name: "normal division",
a: 10,
b: 2,
want: 5,
expectError: false,
}, {
name: "divide by zero",
a: 5,
b: 0,
expectError: true,
errType: DivisionError{},
}, {
name: "invalid divisor",
a: 7,
b: 0,
expectError: true,
errType: DivisionError{},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := SafeDivide(tt.a, tt.b)
if tt.expectError {
if err == nil {
t.Fatal("expected error but got nil")
}
// 精确错误类型匹配
if !errors.As(err, &tt.errType) {
t.Errorf("unexpected error type: %T", err)
}
// 验证错误内容
var divErr DivisionError
if errors.As(err, &divErr) {
if divErr.Dividend != tt.a || divErr.Divisor != tt.b {
t.Errorf("error contains wrong values: %v", divErr)
}
}
} else {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != tt.want {
t.Errorf("got %d, want %d", got, tt.want)
}
}
})
}
}最佳实践
- 用例命名:使用描述性
name字段,测试失败时快速定位 - 错误处理:
- 优先比较错误类型而非字符串内容
- 使用
errors.Is检查sentinel errors - 使用
errors.As提取自定义错误字段
- 资源管理:在
t.Run内使用t.Cleanup释放资源 - 测试组织:
- 正常路径和错误路径分开测试
- 边界值单独作为测试用例
常见错误
- 错误比较不准确:
// 错误做法:直接比较error对象 if err != tt.expectedErr { ... } // 正确做法:使用errors.Is/As if !errors.Is(err, tt.expectedErr) { ... } - 子测试未隔离:未使用
t.Run导致测试状态污染 - 错误处理遗漏:未检查
expectError为true时err为nil的情况 - 过度断言:严格匹配错误字符串(应优先验证错误类型)
扩展知识
- Golden文件:复杂输出可使用
testdata目录存储预期结果 - 测试覆盖率:运行
go test -coverprofile=coverage.out生成报告 - 并行测试:在
t.Run内调用t.Parallel()加速测试 - 测试辅助工具:
cmp.Diff:复杂结构体比较httptest:HTTP处理测试testify/assert:第三方断言库