unit-testing - 表测试 Go 泛型

标签 unit-testing go testing generics

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

如何对泛型函数进行表测试?

我想出了这个 code ,但我需要重新声明每个函数的测试逻辑,因为我无法实例化 T 值。 (在我的项目中,我使用结构而不是 stringint。只是不想包含它们,因为它已经有足够的代码)

你会如何解决这个问题?

编辑: 这是代码:

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])已经提供了合理的抽象级别。正如您所做的那样,您可以在其中放置一些关于开始测试和验证预期结果的通用逻辑。然而,仅此而已。

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

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

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

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

另见 this OP 试图做类似的事情

关于unit-testing - 表测试 Go 泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70980279/

相关文章:

unit-testing - 对在 AngularJS 中定义 Controller 的指令进行单元测试

javascript - 开 Jest - react 自动机 - 未定义不是一个对象(评估 'machine.states' )

java - 为什么 Drools 在使用议程过滤器触发特定规则时验证所有其他规则?

go - 检查网络连接

reflection - 将 reflect.value 转换为 reflect.method

ruby-on-rails - 由于 ArgumentError : wrong number of arguments(1 for 2),Rails 测试失败

.NET NUnit 测试 - Assembly.GetEntryAssembly() 为空

go - 去 revel 未定义:sql或Txn

android - 如何在 Android 中使用 Robotium 测试自定义 ListView 中的复选框

angularjs - 在 Jasmine + Angular 中测试 $httpBackend