suite.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package suite
  2. import (
  3. "flag"
  4. "fmt"
  5. "os"
  6. "reflect"
  7. "regexp"
  8. "runtime/debug"
  9. "testing"
  10. "time"
  11. "github.com/stretchr/testify/assert"
  12. "github.com/stretchr/testify/require"
  13. )
  14. var allTestsFilter = func(_, _ string) (bool, error) { return true, nil }
  15. var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run")
  16. // Suite is a basic testing suite with methods for storing and
  17. // retrieving the current *testing.T context.
  18. type Suite struct {
  19. *assert.Assertions
  20. require *require.Assertions
  21. t *testing.T
  22. }
  23. // T retrieves the current *testing.T context.
  24. func (suite *Suite) T() *testing.T {
  25. return suite.t
  26. }
  27. // SetT sets the current *testing.T context.
  28. func (suite *Suite) SetT(t *testing.T) {
  29. suite.t = t
  30. suite.Assertions = assert.New(t)
  31. suite.require = require.New(t)
  32. }
  33. // Require returns a require context for suite.
  34. func (suite *Suite) Require() *require.Assertions {
  35. if suite.require == nil {
  36. suite.require = require.New(suite.T())
  37. }
  38. return suite.require
  39. }
  40. // Assert returns an assert context for suite. Normally, you can call
  41. // `suite.NoError(expected, actual)`, but for situations where the embedded
  42. // methods are overridden (for example, you might want to override
  43. // assert.Assertions with require.Assertions), this method is provided so you
  44. // can call `suite.Assert().NoError()`.
  45. func (suite *Suite) Assert() *assert.Assertions {
  46. if suite.Assertions == nil {
  47. suite.Assertions = assert.New(suite.T())
  48. }
  49. return suite.Assertions
  50. }
  51. func failOnPanic(t *testing.T) {
  52. r := recover()
  53. if r != nil {
  54. t.Errorf("test panicked: %v\n%s", r, debug.Stack())
  55. t.FailNow()
  56. }
  57. }
  58. // Run provides suite functionality around golang subtests. It should be
  59. // called in place of t.Run(name, func(t *testing.T)) in test suite code.
  60. // The passed-in func will be executed as a subtest with a fresh instance of t.
  61. // Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName.
  62. func (suite *Suite) Run(name string, subtest func()) bool {
  63. oldT := suite.T()
  64. defer suite.SetT(oldT)
  65. return oldT.Run(name, func(t *testing.T) {
  66. suite.SetT(t)
  67. subtest()
  68. })
  69. }
  70. // Run takes a testing suite and runs all of the tests attached
  71. // to it.
  72. func Run(t *testing.T, suite TestingSuite) {
  73. defer failOnPanic(t)
  74. suite.SetT(t)
  75. var suiteSetupDone bool
  76. var stats *SuiteInformation
  77. if _, ok := suite.(WithStats); ok {
  78. stats = newSuiteInformation()
  79. }
  80. tests := []testing.InternalTest{}
  81. methodFinder := reflect.TypeOf(suite)
  82. suiteName := methodFinder.Elem().Name()
  83. for i := 0; i < methodFinder.NumMethod(); i++ {
  84. method := methodFinder.Method(i)
  85. ok, err := methodFilter(method.Name)
  86. if err != nil {
  87. fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err)
  88. os.Exit(1)
  89. }
  90. if !ok {
  91. continue
  92. }
  93. if !suiteSetupDone {
  94. if stats != nil {
  95. stats.Start = time.Now()
  96. }
  97. if setupAllSuite, ok := suite.(SetupAllSuite); ok {
  98. setupAllSuite.SetupSuite()
  99. }
  100. suiteSetupDone = true
  101. }
  102. test := testing.InternalTest{
  103. Name: method.Name,
  104. F: func(t *testing.T) {
  105. parentT := suite.T()
  106. suite.SetT(t)
  107. defer failOnPanic(t)
  108. defer func() {
  109. if stats != nil {
  110. passed := !t.Failed()
  111. stats.end(method.Name, passed)
  112. }
  113. if afterTestSuite, ok := suite.(AfterTest); ok {
  114. afterTestSuite.AfterTest(suiteName, method.Name)
  115. }
  116. if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
  117. tearDownTestSuite.TearDownTest()
  118. }
  119. suite.SetT(parentT)
  120. }()
  121. if setupTestSuite, ok := suite.(SetupTestSuite); ok {
  122. setupTestSuite.SetupTest()
  123. }
  124. if beforeTestSuite, ok := suite.(BeforeTest); ok {
  125. beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name)
  126. }
  127. if stats != nil {
  128. stats.start(method.Name)
  129. }
  130. method.Func.Call([]reflect.Value{reflect.ValueOf(suite)})
  131. },
  132. }
  133. tests = append(tests, test)
  134. }
  135. if suiteSetupDone {
  136. defer func() {
  137. if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
  138. tearDownAllSuite.TearDownSuite()
  139. }
  140. if suiteWithStats, measureStats := suite.(WithStats); measureStats {
  141. stats.End = time.Now()
  142. suiteWithStats.HandleStats(suiteName, stats)
  143. }
  144. }()
  145. }
  146. runTests(t, tests)
  147. }
  148. // Filtering method according to set regular expression
  149. // specified command-line argument -m
  150. func methodFilter(name string) (bool, error) {
  151. if ok, _ := regexp.MatchString("^Test", name); !ok {
  152. return false, nil
  153. }
  154. return regexp.MatchString(*matchMethod, name)
  155. }
  156. func runTests(t testing.TB, tests []testing.InternalTest) {
  157. if len(tests) == 0 {
  158. t.Log("warning: no tests to run")
  159. return
  160. }
  161. r, ok := t.(runner)
  162. if !ok { // backwards compatibility with Go 1.6 and below
  163. if !testing.RunTests(allTestsFilter, tests) {
  164. t.Fail()
  165. }
  166. return
  167. }
  168. for _, test := range tests {
  169. r.Run(test.Name, test.F)
  170. }
  171. }
  172. type runner interface {
  173. Run(name string, f func(t *testing.T)) bool
  174. }