I want to shorten my test code by using a struct for test input and another struct for test desired output for complex tests.
I have my test code like this and it works:
func TestMyTest(*testing.T) {
type Test struct {
n int
items map[string][]int
order int
expect []string
}
tests := []Test{
{
n: 3,
items: map[string][]int{
"item1": []int{1, 2},
"item2": []int{3, 4},
"item3": []int{5, 6},
},
order: 1,
expect: []string{"item1"},
},
// ... more test cases
}
for testNo, test := range tests {
output := myTest(test.n, test.items, test.order)
desire := test.expect
fmt.Printf("Test %v ", testNo+1)
if reflect.DeepEqual(output, desire) {
fmt.Println("PASS")
} else {
fmt.Println("FAIL")
}
fmt.Println("Got:", output, "Expected:", desire)
}
}
My dummy function:
func myTest(n int, items map[string][]int, order int) []string {
res := []string{"item1"}
return res
}
However, if I have more complex input and output, I don't want to type all the parameters out, but instead, I'd like to group them in 1 struct like this:
func TestMyTest2(*testing.T) {
type TestInput struct {
n int
items map[string][]int
order int
}
type TestExpect struct {
expect []string
}
type Test struct {
input TestInput
expect TestExpect
}
tests := []Test{
{
input: TestInput{
n: 3,
items: map[string][]int{
"item1": []int{10, 15},
"item2": []int{3, 4},
"item3": []int{17, 8},
},
order: 1,
},
expect: TestExpect{
[]string{"item3"},
},
},
// ... more test cases
}
for testNo, test := range tests {
output := myTest(test.input) // ERROR: have (TestInput) want (int, map[string][]int, int)
desire := test.expect
fmt.Printf("Test %v ", testNo+1)
if reflect.DeepEqual(output, desire) {
fmt.Println("PASS")
} else {
fmt.Println("FAIL")
}
fmt.Println("Got:", output, "Expected:", desire)
}
}
The error I have:
have (TestInput) want (int, map[string][]int, int)
which makes sense but I've been struggling to "spread" the values inside my TestInput to pass in my function. What I'm trying to do is like in JS, I can do ...params
to "spread" or in Python **params
to "unpack".
I've looked at the answers here, here, here and here but still haven't been able to figure this out.
Use the reflect package to spread the arguments:
func spread(fn interface{}, args interface{}) interface{} {
var in []reflect.Value
s := reflect.Indirect(reflect.ValueOf(args))
for i := 0; i < s.NumField(); i++ {
in = append(in, s.Field(i))
}
out := reflect.ValueOf(fn).Call(in)
return out[0].Interface()
}
The fields in TestInput
must be exported for this to work.
Here's how to use it:
for i, test := range tests {
output := spread(myTest, test.input)
desire := test.expect.expect
if !reflect.DeepEqual(output, desire) {
t.Errorf("%d: got %v, want %v", i, output, desire)
}
}
I think it's simpler to write out the arguments than to use reflection trickery. The code with the arguments written out is faster than the reflection code, but that may not matter for a test.