表测试 Go 泛型

how*_*n3d 4 generics testing unit-testing go

我对 Go 1.18 感到很兴奋,并想测试新的泛型功能。使用起来感觉很简洁,但我偶然发现了一个问题:

如何对通用函数进行表测试?

我想出了这段代码,但我需要重新声明每个函数的测试逻辑,因为我无法实例化T值。(在我的项目中,我使用结构而不是stringand 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)
    }
}

Run Code Online (Sandbox Code Playgroud)

bla*_*een 5

\n

我需要重新声明每个函数的测试逻辑

\n
\n

正确的。

\n

您的函数runTestCase[T Item](tc testCase[T])已经提供了合理的抽象级别。正如您所做的那样,您可以在其中放置一些有关开始测试和验证预期结果的通用逻辑。不过仅此而已。

\n

被测试的通用类型(或函数)迟早必须用某种具体类型实例化,并且一个测试表只能包含这些类型 \xe2\x80\x94 或 / 之一interface{}any您不能使用它来满足特定的约束,例如int | string.

\n

但是,您不需要总是测试每个可能的类型参数。泛型的目的是编写适用于任意类型的代码,特别是约束的目的是编写支持相同操作的任意类型的代码。

\n

我建议仅当代码使用具有不同含义的运算符时才为不同类型编写单元测试。例如:

\n
    \n
  • +数字(求和)和字符串(连接)的运算符
  • \n
  • the<>用于数字(更大、更小)和字符串(按字典顺序排列在之前或之后)
  • \n
\n

另请参阅OP 试图做类似事情的地方

\n