深夜整个人项目,泛型函数单元测试写到吐血了,发帖来吐槽下。单元测试我们知道,一般写法是像下面这样用表驱动测试来写(用到了匿名 struct ):
func Add(a, b int) int {
return a + b
}
// ========== 单元测试分界线 ============
func TestAdd(t *testing.T) {
// 这里定义了一个匿名的 struct ,让代码更简洁容易维护
tests := []struct {
name string
a int
b int
want int
}{
{
name: "ok",
a: 1,
b: 1,
want: 2,
},
{
name: "ok2",
a: 10,
b: 10,
want: 20,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Add(tt.a, tt.b); got != tt.want {
t.Errorf("xxxxxxxxxxxxxxxx")
}
})
}
}
但如果是泛型函数的话,因为目前存在几个问题:
所以泛型函数的单元测试代码就变成了下面这样的写法:
func Add[T constraints.Ordered](a, b T) T {
return a + b
}
// ======== 单元测试分界线 ===========
// 必须在测试函数外单独定义测试用例的结构体
type testCase[T constraints.Ordered] struct {
name string
a T
b T
want T
}
// 同时还必须在测试函数外定义一个执行泛型用例的泛型函数
func runTestCases[T constraints.Ordered](t *testing.T, cases []testCase[T]) {
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
if got := Add(tt.a, tt.b); !reflect.DeepEqual(got, tt.want) {
t.Errorf("xxxxxxxxxxxxxx")
}
})
}
}
// 单元测试函数
func TestAdd(t *testing.T) {
intTestCases := []testCase[int]{
{
name: "ok",
a: 1,
b: 1,
want: 2,
},
{
name: "ok2",
a: 10,
b: 10,
want: 20,
},
}
strCases := []testCase[string]{
{
name: "ok",
a: "A",
b: "B",
want: "AB",
},
{
name: "ok2",
a: "Hello",
b: "World",
want: "HelloWorld",
},
}
runTestCases(t, intTestCases)
runTestCases(t, strCases)
}
也许你会说不就是多定义了个函数还有结构体类型吗,但是我想说的是就是因为这个问题,导致这样子的单元测试代码写起来真的太折磨人了,非常烦人。这段时间我写泛型函数的单元测试都要写吐血了
最重要的是,如果我们在一个文件里定义了多个函数,那么也往往会把他们的单元测试给统一写到同一个 _test.go
文件里。 这种写法导致的结果就是点开一个单元测试代码,里面满眼都是定义在单元测试函数之外的 type xxxTestCase Struct{}
结构体还有 runXXXTestCases[T xxx]()
的泛型函数。可读性和维护起来非常难受。为了可读性解决办法只有一个:给每个泛型函数单独整个 _test.go
文件
嗯,上面就是我的深夜吐槽。不知道今后有没有什么好的工具能结束这种痛苦的写法
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.