Go单元测试中如何验证无效输入的错误场景?
在Go单元测试中正确预期错误的方案
我来帮你梳理下如何在Go单元测试中优雅地预期错误,同时遵循Go的最佳实践(避免直接比较错误字符串)。首先,我们需要调整你的测试用例结构,让它能同时支持预期成功值和预期错误,然后用正确的方式检查错误。
第一步:重构业务函数,使用自定义错误
Go不建议直接比较错误字符串的核心原因是,错误字符串可能会在后续迭代中修改,导致测试脆弱。更好的做法是定义一个可复用的错误变量,这样测试时可以直接比较错误实例:
import "errors" // 定义自定义错误变量,供业务逻辑和测试共享 var ErrIndexOutOfBounds = errors.New("index out of bounds") func arrIdx(arr []int, idx int) (int, error) { if idx >= 0 && idx < len(arr) { return arr[idx], nil } // 返回自定义错误,替代临时的fmt.Errorf return 0, ErrIndexOutOfBounds }
第二步:调整测试用例结构体
我们需要修改测试用例的结构体,让它能明确区分每个用例是预期成功(返回具体值)还是预期错误:
import "testing" func TestArrIdx(t *testing.T) { testArr := []int{1, 2, 3, 4, 5} // 定义测试用例结构体,包含名称、输入、预期值、预期错误 tests := []struct { name string arr []int idx int wantVal int // 预期成功时的返回值 wantErr error // 预期错误时的错误实例 }{ { name: "valid index (last element)", arr: testArr, idx: len(testArr)-1, wantVal: 5, wantErr: nil, // 标记预期无错误 }, { name: "valid index (second element)", arr: testArr, idx: 1, wantVal: 2, wantErr: nil, }, { name: "invalid negative index", arr: testArr, idx: -1, wantVal: 0, // 该值无意义,因为预期错误 wantErr: ErrIndexOutOfBounds, }, { name: "invalid out-of-bounds index", arr: testArr, idx: len(testArr), wantVal: 0, wantErr: ErrIndexOutOfBounds, }, }
第三步:编写测试逻辑,分场景处理预期
在测试循环中,我们需要根据每个用例的预期,分别检查返回值和错误:
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotVal, gotErr := arrIdx(tt.arr, tt.idx) // 场景1:预期无错误 if tt.wantErr == nil { if gotErr != nil { t.Fatalf("arrIdx() returned unexpected error: %v", gotErr) } if gotVal != tt.wantVal { t.Errorf("arrIdx() = %d, want %d", gotVal, tt.wantVal) } return } // 场景2:预期有错误 if gotErr == nil { t.Fatalf("arrIdx() expected error %v, but got nil", tt.wantErr) } // 使用errors.Is检查错误,支持错误包装场景(Go 1.13+) if !errors.Is(gotErr, tt.wantErr) { t.Errorf("arrIdx() error = %v, want %v", gotErr, tt.wantErr) } }) } }
关键要点说明
- 自定义错误变量:这是避免错误字符串比较的核心,让测试更稳定——即使后续错误描述修改,只要错误变量不变,测试依然有效。
- 分场景处理:明确区分预期成功和预期错误的逻辑,让测试代码更易读、易维护。
- errors.Is的使用:如果你的错误存在包装(比如用
fmt.Errorf("wrap: %w", ErrIndexOutOfBounds)),errors.Is依然能正确识别底层的错误实例,比直接比较更健壮。
如果因为维护老代码等原因无法使用自定义错误变量,你可以退而求其次检查错误类型(如果是自定义错误类型),但强烈不建议直接比较错误字符串,因为这种测试非常脆弱。
内容的提问来源于stack exchange,提问作者Viet
相关产品推荐
相关产品推荐

