0

We were brainstorming possible ways of testing generic functions using table driven tests. It seems pretty complicated at the first glance.

What we wanted to achieve is to have a field in the test table struct that can be of whatever type that is accepted by the generic function. It seems however, that you can't use any interface for that.

We came up with the following solution:

Example function:

func PtrTo[T any](t T) *T {
    return &t
}

Example test:

func TestPtrTo(t *testing.T) {

    string1 := "abcd"

    trueVar := true

    testCases := []struct {
        testDescription string
        input           interface{}
        expectedOutput  interface{}
    }{
        {
            testDescription: "string",
            input:           string1,
            expectedOutput:  &string1,
        },
        {
            testDescription: "bool",
            input:           trueVar,
            expectedOutput:  &trueVar,
        },
    }

    for _, testCase := range testCases {
        t.Run(testCase.testDescription, func(t *testing.T) {
            switch concreteTypeInput := testCase.input.(type) {
            case string:
                output := PtrTo(concreteTypeInput)
                assert.Equal(t, testCase.expectedOutput, output)
            case bool:
                output := PtrTo(concreteTypeInput)
                assert.Equal(t, testCase.expectedOutput, output)
            default:
                t.Error("Unexpected type. Please add the type to the switch case")
            }
        })
    }
}

It doesn't really feel optimal, though.

What do you think of this solution?

Do you see any other alternatives?

1 Answers1

-1

A templated helper function could give you the semantics I think you're looking for:

package main

import (
    "testing"

    "github.com/google/go-cmp/cmp"
)

type testCase[T any] struct {
    desc string
    in   T
    want *T
}

func ptrToTest[T any](t *testing.T, tc testCase[T]) {
    t.Helper()
    t.Run(tc.desc, func(t *testing.T) {
        got := PtrTo(tc.in)
        if diff := cmp.Diff(tc.want, got); diff != "" {
            t.Fatalf("got %v, want %v", got, tc.want)
        }
    })
}

func TestPtrTo(t *testing.T) {
    string1 := "abcd"
    ptrToTest(t, testCase[string]{
        desc: "string",
        in:   string1,
        want: &string1,
    })

    trueVar := true
    ptrToTest(t, testCase[bool]{
        desc: "bool",
        in:   trueVar,
        want: &trueVar,
    })
}

That being said, I agree with @blackgreen that the PtrTo function is too trivial for a test like this to be meaningful. Hopefully this is useful for more complicated logic!