mirror of https://github.com/stretchr/testify.git
162 lines
4.3 KiB
Go
162 lines
4.3 KiB
Go
package suite
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"regexp"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
const (
|
|
testPattern = "^Test"
|
|
defaultFormat = "{method}"
|
|
)
|
|
|
|
var (
|
|
matchMethod = flag.String("m", "", "Run only those test methods matching the regular expression.")
|
|
format = flag.String("nameformat", "",
|
|
"The format to use for test name output. Use {function} for the test function name, "+
|
|
"{suite} for the suite type name, and {method} for the test method name. Overrides "+
|
|
"formatting set by NamedRun.")
|
|
)
|
|
|
|
type testIdentifier struct {
|
|
function string
|
|
suite string
|
|
method string
|
|
}
|
|
|
|
func (t testIdentifier) Name(format string) string {
|
|
name := strings.Replace(format, "{function}", t.function, -1)
|
|
name = strings.Replace(name, "{suite}", t.suite, -1)
|
|
name = strings.Replace(name, "{method}", t.method, -1)
|
|
return name
|
|
}
|
|
|
|
// Suite is a basic testing suite with methods for storing and
|
|
// retrieving the current *testing.T context.
|
|
type Suite struct {
|
|
*assert.Assertions
|
|
t *testing.T
|
|
}
|
|
|
|
// T retrieves the current *testing.T context.
|
|
func (suite *Suite) T() *testing.T {
|
|
return suite.t
|
|
}
|
|
|
|
// SetT sets the current *testing.T context.
|
|
func (suite *Suite) SetT(t *testing.T) {
|
|
suite.t = t
|
|
suite.Assertions = assert.New(t)
|
|
}
|
|
|
|
// Run takes a testing suite and runs all of the tests attached
|
|
// to it. For legacy reasons, Run uses a default test name format
|
|
// of "{method}", which only prints out the test method name. See
|
|
// NamedRun for a version of Run that accepts other format strings
|
|
// for the test name in testing output.
|
|
func Run(t *testing.T, suite TestingSuite) {
|
|
run("", t, suite)
|
|
}
|
|
|
|
// NamedRun performs a test run the same as Run, but with a different
|
|
// test name format. Pass a pattern to use as your test names for
|
|
// nameFormat. The following patterns will be replaced by details
|
|
// about the current test:
|
|
//
|
|
// * "{function}": The name of the function that called NamedRun.
|
|
// * "{suite}": The name of the testing suite's underlying type.
|
|
// * "{method}": The name of the test method.
|
|
//
|
|
// Be aware that the test flag "nameformat" will override nameFormat
|
|
// globally.
|
|
func NamedRun(nameFormat string, t *testing.T, suite TestingSuite) {
|
|
run(nameFormat, t, suite)
|
|
}
|
|
|
|
// run is used to ensure that Run and NamedRun (and any other suite
|
|
// running functions) are at the same calling stack depth.
|
|
func run(nameFormat string, t *testing.T, suite TestingSuite) {
|
|
if nameFormat == "" {
|
|
nameFormat = defaultFormat
|
|
}
|
|
if *format != "" {
|
|
nameFormat = *format
|
|
}
|
|
id := testIdentifier{}
|
|
if callPC, _, _, ok := runtime.Caller(2); ok {
|
|
id.function = runtime.FuncForPC(callPC).Name()
|
|
id.function = id.function[strings.LastIndex(id.function, ".")+1:]
|
|
}
|
|
|
|
suite.SetT(t)
|
|
|
|
if setupAllSuite, ok := suite.(SetupAllSuite); ok {
|
|
setupAllSuite.SetupSuite()
|
|
}
|
|
defer func() {
|
|
if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
|
|
tearDownAllSuite.TearDownSuite()
|
|
}
|
|
}()
|
|
|
|
suiteType := reflect.TypeOf(suite)
|
|
methodFinder := suiteType
|
|
if suiteType.Kind() == reflect.Ptr {
|
|
suiteType = suiteType.Elem()
|
|
}
|
|
id.suite = suiteType.Name()
|
|
|
|
tests := []testing.InternalTest{}
|
|
for index := 0; index < methodFinder.NumMethod(); index++ {
|
|
method := methodFinder.Method(index)
|
|
ok, err := methodFilter(method.Name)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err)
|
|
os.Exit(1)
|
|
}
|
|
if ok {
|
|
id.method = method.Name
|
|
test := testing.InternalTest{
|
|
Name: id.Name(nameFormat),
|
|
F: func(t *testing.T) {
|
|
parentT := suite.T()
|
|
suite.SetT(t)
|
|
if setupTestSuite, ok := suite.(SetupTestSuite); ok {
|
|
setupTestSuite.SetupTest()
|
|
}
|
|
defer func() {
|
|
if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
|
|
tearDownTestSuite.TearDownTest()
|
|
}
|
|
suite.SetT(parentT)
|
|
}()
|
|
method.Func.Call([]reflect.Value{reflect.ValueOf(suite)})
|
|
},
|
|
}
|
|
tests = append(tests, test)
|
|
}
|
|
}
|
|
|
|
if !testing.RunTests(func(_, _ string) (bool, error) { return true, nil },
|
|
tests) {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
// methodFilter filters method names similar to `go test -run regexp`. Note
|
|
// that methods must match testPattern prior to matching the passed in regexp.
|
|
func methodFilter(name string) (bool, error) {
|
|
if ok, _ := regexp.MatchString(testPattern, name); !ok {
|
|
return false, nil
|
|
}
|
|
return regexp.MatchString(*matchMethod, name)
|
|
}
|