英文:
Writing unit tests for generics function in golang
问题
我有这个简单的通用函数,用于从map
中检索键:
// getMapKeys 返回一个 map 的键
func getMapKeys[T comparable, U any](m map[T]U) []T {
keys := make([]T, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
return keys
}
我试图编写表驱动的单元测试,如下所示:
var testUnitGetMapKeys = []struct {
name string
inputMap interface{}
expected interface{}
}{
{
name: "string keys",
inputMap: map[string]int{"foo": 1, "bar": 2, "baz": 3},
expected: []string{"foo", "bar", "baz"},
},
{
name: "int keys",
inputMap: map[int]string{1: "foo", 2: "bar", 3: "baz"},
expected: []int{1, 2, 3},
},
{
name: "float64 keys",
inputMap: map[float64]bool{1.0: true, 2.5: false, 3.1415: true},
expected: []float64{1.0, 2.5, 3.1415},
},
}
然而,以下代码失败了:
func (us *UnitUtilSuite) TestUnitGetMapKeys() {
for i := range testUnitGetMapKeys {
us.T().Run(testUnitGetMapKeys[i].name, func(t *testing.T) {
gotKeys := getMapKeys(testUnitGetMapKeys[i].inputMap)
})
}
}
报错信息为:
testUnitGetMapKeys[i].inputMap 的类型 interface{} 与 map[T]U 不匹配(无法推断出 T 和 U)
这可以通过显式转换来修复:
gotKeys := getMapKeys(testUnitGetMapKeys[i].inputMap.(map[string]string))
是否有一种方法可以自动化这些测试,而不必为每个输入测试变量执行显式转换?
英文:
I have this trivial generic function that retrieves the keys from a map
// getMapKeys returns the keys of a map
func getMapKeys[T comparable, U any](m map[T]U) []T {
keys := make([]T, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
return keys
}
I am attempting to write table driven unit tests for it as in:
var testUnitGetMapKeys = []struct {
name string
inputMap interface{}
expected interface{}
}{
{
name: "string keys",
inputMap: map[string]int{"foo": 1, "bar": 2, "baz": 3},
expected: []string{"foo", "bar", "baz"},
},
{
name: "int keys",
inputMap: map[int]string{1: "foo", 2: "bar", 3: "baz"},
expected: []int{1, 2, 3},
},
{
name: "float64 keys",
inputMap: map[float64]bool{1.0: true, 2.5: false, 3.1415: true},
expected: []float64{1.0, 2.5, 3.1415},
},
}
However, the following code fails
func (us *UnitUtilSuite) TestUnitGetMapKeys() {
for i := range testUnitGetMapKeys {
us.T().Run(testUnitGetMapKeys[i].name, func(t *testing.T) {
gotKeys := getMapKeys(testUnitGetMapKeys[i].inputMap)
})
}
}
with
> type interface{} of testUnitGetMapKeys[i].inputMap does not match map[T]U (cannot infer T and U)
This is fixed by explicit casting
gotKeys := getMapKeys(testUnitGetMapKeys[i].inputMap.(map[string]string))
Is there a way to automate these tests rather than having to perform explicit casting for each input test variable?
答案1
得分: 1
请注意,除非你的通用函数除了通用逻辑之外还执行了一些特定类型的逻辑,否则通过针对不同类型测试该函数不会带来任何好处。该函数的通用逻辑对类型参数的类型集中的所有类型都是相同的,因此可以通过单一类型完全测试该逻辑。
但是,如果你仍然想针对不同类型运行测试,你可以简单地按照以下方式进行操作:
var testUnitGetMapKeys = []struct {
name string
got any
want any
}{
{
name: "string keys",
got: getMapKeys(map[string]int{"foo": 1, "bar": 2, "baz": 3}),
want: []string{"foo", "bar", "baz"},
},
{
name: "int keys",
got: getMapKeys(map[int]string{1: "foo", 2: "bar", 3: "baz"}),
want: []int{1, 2, 3},
},
{
name: "float64 keys",
got: getMapKeys(map[float64]bool{1.0: true, 2.5: false, 3.1415: true}),
want: []float64{1.0, 2.5, 3.1415},
},
}
// ...
func (us *UnitUtilSuite) TestUnitGetMapKeys() {
for _, tt := range testUnitGetMapKeys {
us.T().Run(tt.name, func(t *testing.T) {
if !reflect.DeepEqual(tt.got, tt.want) {
t.Errorf("got=%v; want=%v", tt.got, tt.want)
}
})
}
}
英文:
Note that, unless your generic function is performing some type-specific logic in addition to the generic logic, you gain nothing by testing that function against different types. The function's generic logic is identical for all types in the type parameter's type set and can therefore be fully exercised with a single type.
But if you want to run the tests against different types anyway, you can simply do the following:
var testUnitGetMapKeys = []struct {
name string
got any
want any
}{
{
name: "string keys",
got: getMapKeys(map[string]int{"foo": 1, "bar": 2, "baz": 3}),
want: []string{"foo", "bar", "baz"},
},
{
name: "int keys",
got: getMapKeys(map[int]string{1: "foo", 2: "bar", 3: "baz"}),
want: []int{1, 2, 3},
},
{
name: "float64 keys",
got: getMapKeys(map[float64]bool{1.0: true, 2.5: false, 3.1415: true}),
want: []float64{1.0, 2.5, 3.1415},
},
}
// ...
func (us *UnitUtilSuite) TestUnitGetMapKeys() {
for _, tt := range testUnitGetMapKeys {
us.T().Run(tt.name, func(t *testing.T) {
if !reflect.DeepEqual(tt.got, tt.want) {
t.Errorf("got=%v; want=%v", tt.got, tt.want)
}
})
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论