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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
// Licensed under the MIT license, see LICENSE file for details.
/*
Package qtsuite allows quicktest to run test suites.
A test suite is a value with one or more test methods.
For example, the following code defines a suite of test functions that starts
an HTTP server before running each test, and tears it down afterwards:
type suite struct {
url string
}
func (s *suite) Init(c *qt.C) {
hnd := func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "%s %s", req.Method, req.URL.Path)
}
srv := httptest.NewServer(http.HandlerFunc(hnd))
c.Cleanup(srv.Close)
s.url = srv.URL
}
func (s *suite) TestGet(c *qt.C) {
c.Parallel()
resp, err := http.Get(s.url)
c.Assert(err, qt.Equals, nil)
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
c.Assert(err, qt.Equals, nil)
c.Assert(string(b), qt.Equals, "GET /")
}
func (s *suite) TestHead(c *qt.C) {
c.Parallel()
resp, err := http.Head(s.url + "/path")
c.Assert(err, qt.Equals, nil)
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
c.Assert(err, qt.Equals, nil)
c.Assert(string(b), qt.Equals, "")
c.Assert(resp.ContentLength, qt.Equals, int64(10))
}
The above code could be invoked from a test function like this:
func TestHTTPMethods(t *testing.T) {
qtsuite.Run(qt.New(t), &suite{"http://example.com"})
}
*/
package qtsuite
import (
"reflect"
"strings"
"unicode"
"unicode/utf8"
qt "github.com/frankban/quicktest"
)
// Run runs each test method defined on the given value as a separate
// subtest. A test is a method of the form
//
// func (T) TestXxx(*quicktest.C)
//
// where Xxx does not start with a lowercase letter.
//
// If suite is a pointer, the value pointed to is copied before any
// methods are invoked on it; a new copy is made for each test. This
// means that it is OK for tests to modify fields in suite concurrently
// if desired - it's OK to call c.Parallel().
//
// If suite has a method of the form
//
// func (T) Init(*quicktest.C)
//
// this method will be invoked before each test run.
func Run(c *qt.C, suite interface{}) {
sv := reflect.ValueOf(suite)
st := sv.Type()
init, hasInit := st.MethodByName("Init")
if hasInit && !isValidMethod(init) {
c.Fatal("wrong signature for Init, must be Init(*quicktest.C)")
}
for i := 0; i < st.NumMethod(); i++ {
m := st.Method(i)
if !isTestMethod(m) {
continue
}
c.Run(m.Name, func(c *qt.C) {
if !isValidMethod(m) {
c.Fatalf("wrong signature for %s, must be %s(*quicktest.C)", m.Name, m.Name)
}
sv := sv
if st.Kind() == reflect.Ptr {
sv1 := reflect.New(st.Elem())
sv1.Elem().Set(sv.Elem())
sv = sv1
}
args := []reflect.Value{sv, reflect.ValueOf(c)}
if hasInit {
init.Func.Call(args)
}
m.Func.Call(args)
})
}
}
var cType = reflect.TypeOf(&qt.C{})
func isTestMethod(m reflect.Method) bool {
if !strings.HasPrefix(m.Name, "Test") {
return false
}
r, n := utf8.DecodeRuneInString(m.Name[4:])
return n == 0 || !unicode.IsLower(r)
}
func isValidMethod(m reflect.Method) bool {
return m.Type.NumIn() == 2 && m.Type.NumOut() == 0 && m.Type.In(1) == cType
}
|