Table 测试 Go 泛型
Table Testing Go Generics
我对 Go 1.18 感到兴奋并想测试新的泛型特性。
使用起来感觉很整洁,但我偶然发现了一个问题:
你如何table测试泛型函数?
我想出了这个 code,但我需要重新声明每个函数的测试逻辑,因为我无法实例化 T
值。
(在我的项目中,我使用结构而不是 string
和 int
。只是不想包含它们,因为它已经有足够的代码)
你会如何解决这个问题?
编辑:
这是代码:
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
type Item interface {
int | string
}
type store[T Item] map[int64]T
// add adds an Item to the map if the id of the Item isn't present already
func (s store[T]) add(key int64, val T) {
_, exists := s[key]
if exists {
return
}
s[key] = val
}
func TestStore(t *testing.T) {
t.Run("ints", testInt)
t.Run("strings", testString)
}
type testCase[T Item] struct {
name string
start store[T]
key int64
val T
expected store[T]
}
func testString(t *testing.T) {
t.Parallel()
tests := []testCase[string]{
{
name: "empty map",
start: store[string]{},
key: 123,
val: "test",
expected: store[string]{
123: "test",
},
},
{
name: "existing key",
start: store[string]{
123: "test",
},
key: 123,
val: "newVal",
expected: store[string]{
123: "test",
},
},
}
for _, tc := range tests {
t.Run(tc.name, runTestCase(tc))
}
}
func testInt(t *testing.T) {
t.Parallel()
tests := []testCase[int]{
{
name: "empty map",
start: store[int]{},
key: 123,
val: 456,
expected: store[int]{
123: 456,
},
},
{
name: "existing key",
start: store[int]{
123: 456,
},
key: 123,
val: 999,
expected: store[int]{
123: 456,
},
},
}
for _, tc := range tests {
t.Run(tc.name, runTestCase(tc))
}
}
func runTestCase[T Item](tc testCase[T]) func(t *testing.T) {
return func(t *testing.T) {
tc.start.add(tc.key, tc.val)
assert.Equal(t, tc.start, tc.expected)
}
}
I need to redeclare my testing logic over each function
正确。
您的函数 runTestCase[T Item](tc testCase[T])
已经提供了合理的抽象级别。正如您所做的那样,您可以在其中放置一些关于开始测试和验证预期结果的通用逻辑。不过仅此而已。
测试中的泛型类型(或函数)迟早要用某种具体类型实例化,并且一个测试 table 只能包括其中一种类型 — 或者 interface{}
/any
,你不能用它来满足像 int | string
.
这样的特定约束
但是,您可能不需要总是测试每个可能的类型参数。泛型的目的是编写适用于任意类型的代码,特别是约束的目的是编写支持相同操作的任意类型的代码。
我建议仅当代码使用具有不同含义的运算符时才为不同类型编写单元测试。例如:
- 数字(求和)和字符串(连接)的
+
运算符
<
和 >
用于数字(大于、小于)和字符串(按字典顺序在前后)
另见 OP 试图做类似的事情
我对 Go 1.18 感到兴奋并想测试新的泛型特性。 使用起来感觉很整洁,但我偶然发现了一个问题:
你如何table测试泛型函数?
我想出了这个 code,但我需要重新声明每个函数的测试逻辑,因为我无法实例化 T
值。
(在我的项目中,我使用结构而不是 string
和 int
。只是不想包含它们,因为它已经有足够的代码)
你会如何解决这个问题?
编辑: 这是代码:
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
type Item interface {
int | string
}
type store[T Item] map[int64]T
// add adds an Item to the map if the id of the Item isn't present already
func (s store[T]) add(key int64, val T) {
_, exists := s[key]
if exists {
return
}
s[key] = val
}
func TestStore(t *testing.T) {
t.Run("ints", testInt)
t.Run("strings", testString)
}
type testCase[T Item] struct {
name string
start store[T]
key int64
val T
expected store[T]
}
func testString(t *testing.T) {
t.Parallel()
tests := []testCase[string]{
{
name: "empty map",
start: store[string]{},
key: 123,
val: "test",
expected: store[string]{
123: "test",
},
},
{
name: "existing key",
start: store[string]{
123: "test",
},
key: 123,
val: "newVal",
expected: store[string]{
123: "test",
},
},
}
for _, tc := range tests {
t.Run(tc.name, runTestCase(tc))
}
}
func testInt(t *testing.T) {
t.Parallel()
tests := []testCase[int]{
{
name: "empty map",
start: store[int]{},
key: 123,
val: 456,
expected: store[int]{
123: 456,
},
},
{
name: "existing key",
start: store[int]{
123: 456,
},
key: 123,
val: 999,
expected: store[int]{
123: 456,
},
},
}
for _, tc := range tests {
t.Run(tc.name, runTestCase(tc))
}
}
func runTestCase[T Item](tc testCase[T]) func(t *testing.T) {
return func(t *testing.T) {
tc.start.add(tc.key, tc.val)
assert.Equal(t, tc.start, tc.expected)
}
}
I need to redeclare my testing logic over each function
正确。
您的函数 runTestCase[T Item](tc testCase[T])
已经提供了合理的抽象级别。正如您所做的那样,您可以在其中放置一些关于开始测试和验证预期结果的通用逻辑。不过仅此而已。
测试中的泛型类型(或函数)迟早要用某种具体类型实例化,并且一个测试 table 只能包括其中一种类型 — 或者 interface{}
/any
,你不能用它来满足像 int | string
.
但是,您可能不需要总是测试每个可能的类型参数。泛型的目的是编写适用于任意类型的代码,特别是约束的目的是编写支持相同操作的任意类型的代码。
我建议仅当代码使用具有不同含义的运算符时才为不同类型编写单元测试。例如:
- 数字(求和)和字符串(连接)的
+
运算符 <
和>
用于数字(大于、小于)和字符串(按字典顺序在前后)
另见