/
suite.go
85 lines (70 loc) · 2.25 KB
/
suite.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package suite
import (
"reflect"
"runtime/debug"
"strings"
"testing"
)
// TestFunc defines the test function in a test case
type TestFunc func(*testing.T)
// SetUpFunc sets up provider-specific resource in the test suite
type SetUpFunc func() error
// TeardownFunc tears down provider-specific resources from the test suite
type TeardownFunc func() error
// ShouldSkipTestFunc determines whether the test suite should skip certain tests
type ShouldSkipTestFunc func(string) bool
// TestSuite contains methods that defines the lifecycle of a test suite
type TestSuite interface {
Setup(t *testing.T)
Teardown()
}
// TestSkipper allows providers to skip certain tests
type TestSkipper interface {
ShouldSkipTest(string) bool
}
type testCase struct {
name string
f TestFunc
}
// Run runs tests registered in the test suite
func Run(t *testing.T, ts TestSuite) {
defer failOnPanic(t)
ts.Setup(t)
defer ts.Teardown()
// The implementation below is based on https://github.com/stretchr/testify
testFinder := reflect.TypeOf(ts)
tests := []testCase{}
for i := 0; i < testFinder.NumMethod(); i++ {
method := testFinder.Method(i)
if !isValidTestFunc(method) {
continue
}
test := testCase{
name: method.Name,
f: func(t *testing.T) {
defer failOnPanic(t)
if tSkipper, ok := ts.(TestSkipper); ok && tSkipper.ShouldSkipTest(method.Name) {
t.Skipf("Skipped due to shouldSkipTest()")
}
method.Func.Call([]reflect.Value{reflect.ValueOf(ts), reflect.ValueOf(t)})
},
}
tests = append(tests, test)
}
for _, test := range tests {
t.Run(test.name, test.f)
}
}
// failOnPanic recovers panic occurred in the test suite and marks the test / test suite as failed
func failOnPanic(t *testing.T) {
if r := recover(); r != nil {
t.Fatalf("%v\n%s", r, debug.Stack())
}
}
// isValidTestFunc determines whether or not a given method is a valid test function
func isValidTestFunc(method reflect.Method) bool {
return strings.HasPrefix(method.Name, "Test") && // Test function name must start with "Test",
method.Type.NumIn() == 2 && // the number of function input should be 2 (*TestSuite ts and t *testing.T),
method.Type.In(1) == reflect.TypeOf(&testing.T{}) &&
method.Type.NumOut() == 0 // and the number of function output should be 0
}