From ef68399acb020c4d33a1881a28f44f9f4cd4aaf1 Mon Sep 17 00:00:00 2001 From: Aniruddha Maru Date: Mon, 29 Nov 2021 21:22:40 +0530 Subject: [PATCH] Remove suite.Suite interface --- README.md | 21 ++- suite/doc.go | 25 ++-- suite/interfaces.go | 32 +---- suite/suite.go | 275 ++++++++++++++++++++++------------------ suite/suite_test.go | 303 ++++++++++++++++++++------------------------ 5 files changed, 315 insertions(+), 341 deletions(-) diff --git a/README.md b/README.md index c78250e41..754832846 100644 --- a/README.md +++ b/README.md @@ -211,11 +211,9 @@ import ( "github.com/stretchr/testify/suite" ) -// Define the suite, and absorb the built-in basic suite -// functionality from testify - including a T() method which -// returns the current testing context +// Define the suite, which is simply a struct with all +// fields that tests need. type ExampleTestSuite struct { - suite.Suite VariableThatShouldStartAtFive int } @@ -227,8 +225,8 @@ func (suite *ExampleTestSuite) SetupTest() { // All methods that begin with "Test" are run as tests within a // suite. -func (suite *ExampleTestSuite) TestExample() { - assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) +func (suite *ExampleTestSuite) TestExample(t *suite.T) { + assert.Equal(t, 5, suite.VariableThatShouldStartAtFive) } // In order for 'go test' to run this suite, we need to create @@ -251,23 +249,22 @@ import ( "github.com/stretchr/testify/suite" ) -// Define the suite, and absorb the built-in basic suite -// functionality from testify - including assertion methods. +// Define the suite, which is simply a struct with all +// fields that tests need. type ExampleTestSuite struct { - suite.Suite VariableThatShouldStartAtFive int } // Make sure that VariableThatShouldStartAtFive is set to five // before each test -func (suite *ExampleTestSuite) SetupTest() { +func (suite *ExampleTestSuite) SetupTest(t *suite.T) { suite.VariableThatShouldStartAtFive = 5 } // All methods that begin with "Test" are run as tests within a // suite. -func (suite *ExampleTestSuite) TestExample() { - suite.Equal(suite.VariableThatShouldStartAtFive, 5) +func (suite *ExampleTestSuite) TestExample(t *suite.T) { + t.Equal(5, suite.VariableThatShouldStartAtFive) } // In order for 'go test' to run this suite, we need to create diff --git a/suite/doc.go b/suite/doc.go index 433c3adc7..7b992d5d6 100644 --- a/suite/doc.go +++ b/suite/doc.go @@ -5,12 +5,15 @@ // or individual tests (depending on which interface(s) you // implement). // -// A testing suite is usually built by first extending the built-in -// suite functionality from suite.Suite in testify. +// A testing suite is usually built by defining a Suite struct +// that includes all fields that tests need. // // After that, you can implement any of the interfaces in // suite/interfaces.go to add setup/teardown functionality to your // suite, and add any methods that start with "Test" to add tests. +// Test methods must match signature: `func(*suite.T)`. The suite.T +// object passed may be used to run sub-tests, verify assertions +// and control test execution. // Methods that do not match any suite interfaces and do not begin // with "Test" will not be run by testify, and can safely be used as // helper methods. @@ -20,10 +23,6 @@ // identity that "go test" is already looking for (i.e. // func(*testing.T)). // -// To be able to run parallel sub-tests, your testing suite should -// implement "CopySuite". This may or may not be a deepcopy depending -// on the fields in the struct. -// // Regular expression to select test suites specified command-line // argument "-run". Regular expression to select the methods // of test suites specified command-line argument "-m". @@ -37,25 +36,23 @@ // "github.com/stretchr/testify/suite" // ) // -// // Define the suite, and absorb the built-in basic suite -// // functionality from testify - including a T() method which -// // returns the current testing context +// // Define the suite, which is simply a struct with all +// // fields that tests need. // type ExampleTestSuite struct { -// suite.Suite // VariableThatShouldStartAtFive int // } // // // Make sure that VariableThatShouldStartAtFive is set to five // // before each test -// func (suite *ExampleTestSuite) SetupTest() { +// func (suite *ExampleTestSuite) SetupTest(t *suite.T) { // suite.VariableThatShouldStartAtFive = 5 // } // // // All methods that begin with "Test" are run as tests within a // // suite. -// func (suite *ExampleTestSuite) TestExample() { -// assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) -// suite.Equal(5, suite.VariableThatShouldStartAtFive) +// func (suite *ExampleTestSuite) TestExample(t *suite.T) { +// assert.Equal(t, 5, suite.VariableThatShouldStartAtFive) +// t.Equal(5, suite.VariableThatShouldStartAtFive) // } // // // In order for 'go test' to run this suite, we need to create diff --git a/suite/interfaces.go b/suite/interfaces.go index bec7a47cf..55f1210f7 100644 --- a/suite/interfaces.go +++ b/suite/interfaces.go @@ -1,62 +1,44 @@ package suite -import "testing" - -// TestingSuite can store and return the current *testing.T context -// generated by 'go test'. -type TestingSuite interface { - T() *testing.T - setT(*testing.T) - clearT() -} - -// CopySuite indicates a copyable struct, deepcopy vs shallow is -// implementation detail of the application. -type CopySuite interface { - // Copy creates a copy of the calling suite object. The returned - // object must be the same concrete type as caller - Copy() TestingSuite -} - // SetupAllSuite has a SetupSuite method, which will run before the // tests in the suite are run. type SetupAllSuite interface { - SetupSuite() + SetupSuite(t *T) } // SetupTestSuite has a SetupTest method, which will run before each // test in the suite. type SetupTestSuite interface { - SetupTest() + SetupTest(t *T) } // TearDownAllSuite has a TearDownSuite method, which will run after // all the tests in the suite have been run. type TearDownAllSuite interface { - TearDownSuite() + TearDownSuite(t *T) } // TearDownTestSuite has a TearDownTest method, which will run after // each test in the suite. type TearDownTestSuite interface { - TearDownTest() + TearDownTest(t *T) } // BeforeTest has a function to be executed right before the test // starts and receives the suite and test names as input type BeforeTest interface { - BeforeTest(suiteName, testName string) + BeforeTest(t *T, suiteName, testName string) } // AfterTest has a function to be executed right after the test // finishes and receives the suite and test names as input type AfterTest interface { - AfterTest(suiteName, testName string) + AfterTest(t *T, suiteName, testName string) } // WithStats implements HandleStats, a function that will be executed // when a test suite is finished. The stats contain information about // the execution of that suite and its tests. type WithStats interface { - HandleStats(suiteName string, stats *SuiteInformation) + HandleStats(t *T, suiteName string, stats *SuiteInformation) } diff --git a/suite/suite.go b/suite/suite.go index b3a61ae6e..7be9a3d16 100644 --- a/suite/suite.go +++ b/suite/suite.go @@ -14,63 +14,109 @@ import ( "github.com/stretchr/testify/require" ) -var allTestsFilter = func(_, _ string) (bool, error) { return true, nil } var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run") -// Suite is a basic testing suite with methods for storing and -// retrieving the current *testing.T context. -type Suite struct { +// T is a basic testing suite with methods for accessing +// appropriate *testing.T objects in tests. +type T struct { *assert.Assertions - require *require.Assertions - t *testing.T + require *require.Assertions + testingT *testing.T } -// T retrieves the current *testing.T context. -func (suite *Suite) T() *testing.T { - return suite.t +func (t *T) Cleanup(f func()) { + t.testingT.Cleanup(f) } -// setT sets the current *testing.T context. -func (suite *Suite) setT(t *testing.T) { - if suite.t != nil { - panic("suite.t already set, can't overwrite") - } - suite.t = t - suite.Assertions = assert.New(t) - suite.require = require.New(t) +func (t *T) Error(args ...interface{}) { + t.testingT.Error(args...) } -func (suite *Suite) clearT() { - suite.t = nil - suite.Assertions = nil - suite.require = nil +func (t *T) Errorf(format string, args ...interface{}) { + t.testingT.Errorf(format, args...) } -// Require returns a require context for suite. -func (suite *Suite) Require() *require.Assertions { - if suite.require == nil { - suite.require = require.New(suite.T()) +func (t *T) Fail() { + t.testingT.Fail() +} + +func (t *T) FailNow() { + t.testingT.FailNow() +} + +func (t *T) Failed() bool { + return t.testingT.Failed() +} + +func (t *T) Fatal(args ...interface{}) { + t.testingT.Fatal(args...) +} + +func (t *T) Fatalf(format string, args ...interface{}) { + t.testingT.Fatalf(format, args...) +} + +func (t *T) Helper() { + t.testingT.Helper() +} + +func (t *T) Log(args ...interface{}) { + t.testingT.Log(args...) +} + +func (t *T) Logf(format string, args ...interface{}) { + t.testingT.Logf(format, args...) +} + +func (t *T) Name() string { + return t.testingT.Name() +} +func (t *T) Skip(args ...interface{}) { + t.testingT.Skip(args...) +} + +func (t *T) SkipNow() { + t.testingT.SkipNow() +} + +func (t *T) Skipf(format string, args ...interface{}) { + t.testingT.Skipf(format, args...) +} +func (t *T) Skipped() bool { + return t.testingT.Skipped() +} + +func (t *T) TempDir() string { + return t.testingT.TempDir() +} + +func (t *T) Parallel() { + t.testingT.Parallel() +} + +// setT sets the current *testing.T context. +func (t *T) setT(testingT *testing.T) { + if t.testingT != nil { + panic("T.testingT already set, can't overwrite") } - return suite.require + t.testingT = testingT + t.Assertions = assert.New(testingT) + t.require = require.New(testingT) } -// Assert returns an assert context for suite. Normally, you can call -// `suite.NoError(expected, actual)`, but for situations where the embedded -// methods are overridden (for example, you might want to override -// assert.Assertions with require.Assertions), this method is provided so you -// can call `suite.Assert().NoError()`. -func (suite *Suite) Assert() *assert.Assertions { - if suite.Assertions == nil { - suite.Assertions = assert.New(suite.T()) +// Require returns a require context for suite. +func (t *T) Require() *require.Assertions { + if t.testingT == nil { + panic("T.testingT not set, can't get Require object") } - return suite.Assertions + return t.require } -func failOnPanic(t *testing.T) { +func failOnPanic(t *T) { r := recover() if r != nil { t.Errorf("test panicked: %v\n%s", r, debug.Stack()) - t.FailNow() + t.testingT.FailNow() } } @@ -78,23 +124,20 @@ func failOnPanic(t *testing.T) { // called in place of t.Run(name, func(t *testing.T)) in test suite code. // The passed-in func will be executed as a subtest with a fresh instance of t. // Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName. -// Deprecated: This method doesn't handle parallel sub-tests and will be removed in v2. -func (suite *Suite) Run(name string, subtest func()) bool { - oldT := suite.T() - defer func() { - suite.clearT() - suite.setT(oldT) - }() - return oldT.Run(name, func(t *testing.T) { - suite.clearT() - suite.setT(t) - subtest() +func (t *T) Run(name string, subtest func(t *T)) bool { + return t.testingT.Run(name, func(testingT *testing.T) { + t := &T{} + t.setT(testingT) + subtest(t) }) } // Run takes a testing suite and runs all of the tests attached // to it. -func Run(t *testing.T, suite TestingSuite) { +func Run(testingT *testing.T, suite interface{}) { + t := &T{} + t.setT(testingT) + defer failOnPanic(t) var suiteSetupDone bool @@ -108,93 +151,90 @@ func Run(t *testing.T, suite TestingSuite) { methodFinder := reflect.TypeOf(suite) suiteName := methodFinder.Elem().Name() - t.Run("All", func(t *testing.T) { - defer failOnPanic(t) + for i := 0; i < methodFinder.NumMethod(); i++ { + method := methodFinder.Method(i) - suite.setT(t) + ok, err := methodFilter(method.Name) + if err != nil { + fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err) + os.Exit(1) + } - for i := 0; i < methodFinder.NumMethod(); i++ { - method := methodFinder.Method(i) + if !ok { + continue + } - ok, err := methodFilter(method.Name) - if err != nil { - fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err) - os.Exit(1) + if !suiteSetupDone { + if stats != nil { + stats.Start = time.Now() } - if !ok { - continue + if setupAllSuite, ok := suite.(SetupAllSuite); ok { + setupAllSuite.SetupSuite(t) } - if !suiteSetupDone { - if stats != nil { - stats.Start = time.Now() - } + suiteSetupDone = true + } - if setupAllSuite, ok := suite.(SetupAllSuite); ok { - setupAllSuite.SetupSuite() - } + test := testing.InternalTest{ + Name: method.Name, + F: func(testingT *testing.T) { + t := &T{} + t.setT(testingT) - suiteSetupDone = true - } + defer failOnPanic(t) - test := testing.InternalTest{ - Name: method.Name, - F: func(t *testing.T) { - defer failOnPanic(t) - childSuite := suite - if c, ok := suite.(CopySuite); ok { - childSuite = c.Copy() - childSuite.clearT() - } - childSuite.setT(t) - defer func() { - if _, ok := suite.(CopySuite); !ok { - defer suite.clearT() - } - if stats != nil { - passed := !t.Failed() - stats.end(method.Name, passed) - } - - if tearDownTestSuite, ok := childSuite.(TearDownTestSuite); ok { - tearDownTestSuite.TearDownTest() - } - - if afterTestSuite, ok := childSuite.(AfterTest); ok { - afterTestSuite.AfterTest(suiteName, method.Name) - } - }() - - if beforeTestSuite, ok := childSuite.(BeforeTest); ok { - beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name) + defer func() { + if stats != nil { + passed := !t.testingT.Failed() + stats.end(method.Name, passed) } - if setupTestSuite, ok := childSuite.(SetupTestSuite); ok { - setupTestSuite.SetupTest() + + if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok { + tearDownTestSuite.TearDownTest(t) } - if stats != nil { - stats.start(method.Name) + if afterTestSuite, ok := suite.(AfterTest); ok { + afterTestSuite.AfterTest(t, suiteName, method.Name) } + }() - method.Func.Call([]reflect.Value{reflect.ValueOf(childSuite)}) - }, - } - tests = append(tests, test) + if beforeTestSuite, ok := suite.(BeforeTest); ok { + beforeTestSuite.BeforeTest(t, methodFinder.Elem().Name(), method.Name) + } + if setupTestSuite, ok := suite.(SetupTestSuite); ok { + setupTestSuite.SetupTest(t) + } + + if stats != nil { + stats.start(method.Name) + } + + method.Func.Call([]reflect.Value{reflect.ValueOf(suite), reflect.ValueOf(t)}) + }, } + tests = append(tests, test) + } + + if len(tests) == 0 { + testingT.Log("warning: no tests to run") + return + } - suite.clearT() - runTests(t, tests) + // run sub-tests in a group so tearDownSuite is called in the right order + testingT.Run("All", func(testingT *testing.T) { + for _, test := range tests { + testingT.Run(test.Name, test.F) + } }) if suiteSetupDone { - suite.setT(t) if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok { - tearDownAllSuite.TearDownSuite() + tearDownAllSuite.TearDownSuite(t) } if suiteWithStats, measureStats := suite.(WithStats); measureStats { stats.End = time.Now() - suiteWithStats.HandleStats(suiteName, stats) + suiteWithStats.HandleStats(t, suiteName, stats) } } } @@ -207,14 +247,3 @@ func methodFilter(name string) (bool, error) { } return regexp.MatchString(*matchMethod, name) } - -func runTests(t *testing.T, tests []testing.InternalTest) { - if len(tests) == 0 { - t.Log("warning: no tests to run") - return - } - - for _, test := range tests { - t.Run(test.Name, test.F) - } -} diff --git a/suite/suite_test.go b/suite/suite_test.go index 8f584de4c..106234870 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -1,10 +1,13 @@ -package suite +package suite_test import ( "bytes" "errors" "flag" "fmt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "io/ioutil" "math/rand" "os" @@ -13,14 +16,13 @@ import ( "sync" "testing" "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) // SuiteRequireTwice is intended to test the usage of suite.Require in two // different tests -type SuiteRequireTwice struct{ Suite } +type SuiteRequireTwice struct{} + +var allTestsFilter = func(_, _ string) (bool, error) { return true, nil } // TestSuiteRequireTwice checks for regressions of issue #149 where // suite.requirements was not initialised in suite.SetT() @@ -31,26 +33,24 @@ func TestSuiteRequireTwice(t *testing.T) { []testing.InternalTest{{ Name: "TestSuiteRequireTwice", F: func(t *testing.T) { - suite := new(SuiteRequireTwice) - Run(t, suite) + suite.Run(t, new(SuiteRequireTwice)) }, }}, ) assert.Equal(t, false, ok) } -func (s *SuiteRequireTwice) TestRequireOne() { - r := s.Require() +func (s *SuiteRequireTwice) TestRequireOne(t *suite.T) { + r := t.Require() r.Equal(1, 2) } -func (s *SuiteRequireTwice) TestRequireTwo() { - r := s.Require() +func (s *SuiteRequireTwice) TestRequireTwo(t *suite.T) { + r := t.Require() r.Equal(1, 2) } type panickingSuite struct { - Suite panicInSetupSuite bool panicInSetupTest bool panicInBeforeTest bool @@ -78,7 +78,7 @@ func (s *panickingSuite) BeforeTest(_, _ string) { } } -func (s *panickingSuite) Test() { +func (s *panickingSuite) Test(t *suite.T) { if s.panicInTest { panic("oops in test") } @@ -107,31 +107,31 @@ func TestSuiteRecoverPanic(t *testing.T) { panickingTests := []testing.InternalTest{ { Name: "TestPanicInSetupSuite", - F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupSuite: true}) }, + F: func(t *testing.T) { suite.Run(t, &panickingSuite{panicInSetupSuite: true}) }, }, { Name: "TestPanicInSetupTest", - F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupTest: true}) }, + F: func(t *testing.T) { suite.Run(t, &panickingSuite{panicInSetupTest: true}) }, }, { Name: "TestPanicInBeforeTest", - F: func(t *testing.T) { Run(t, &panickingSuite{panicInBeforeTest: true}) }, + F: func(t *testing.T) { suite.Run(t, &panickingSuite{panicInBeforeTest: true}) }, }, { Name: "TestPanicInTest", - F: func(t *testing.T) { Run(t, &panickingSuite{panicInTest: true}) }, + F: func(t *testing.T) { suite.Run(t, &panickingSuite{panicInTest: true}) }, }, { Name: "TestPanicInAfterTest", - F: func(t *testing.T) { Run(t, &panickingSuite{panicInAfterTest: true}) }, + F: func(t *testing.T) { suite.Run(t, &panickingSuite{panicInAfterTest: true}) }, }, { Name: "TestPanicInTearDownTest", - F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownTest: true}) }, + F: func(t *testing.T) { suite.Run(t, &panickingSuite{panicInTearDownTest: true}) }, }, { Name: "TestPanicInTearDownSuite", - F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownSuite: true}) }, + F: func(t *testing.T) { suite.Run(t, &panickingSuite{panicInTearDownSuite: true}) }, }, } @@ -149,9 +149,6 @@ func TestSuiteRecoverPanic(t *testing.T) { // more like a real world example, all tests in the suite perform some // type of assertion. type SuiteTester struct { - // Include our basic suite logic. - Suite - // Keep counts of how many times each method is run. SetupSuiteRunCount int TearDownSuiteRunCount int @@ -171,157 +168,152 @@ type SuiteTester struct { TimeBefore []time.Time TimeAfter []time.Time - SuiteT *testing.T - SuiteGroupT *testing.T - TestT map[string]*testing.T + SuiteT *suite.T + TestT map[string]*suite.T } // The SetupSuite method will be run by testify once, at the very // start of the testing suite, before any tests are run. -func (suite *SuiteTester) SetupSuite() { - suite.SetupSuiteRunCount++ - suite.SuiteGroupT = suite.T() +func (s *SuiteTester) SetupSuite(t *suite.T) { + s.SetupSuiteRunCount++ + s.SuiteT = t } -func (suite *SuiteTester) BeforeTest(suiteName, testName string) { - suite.SuiteNameBefore = append(suite.SuiteNameBefore, suiteName) - suite.TestNameBefore = append(suite.TestNameBefore, testName) - suite.TimeBefore = append(suite.TimeBefore, time.Now()) - suite.TestT[testName] = suite.T() +func (s *SuiteTester) BeforeTest(t *suite.T, suiteName, testName string) { + s.SuiteNameBefore = append(s.SuiteNameBefore, suiteName) + s.TestNameBefore = append(s.TestNameBefore, testName) + s.TimeBefore = append(s.TimeBefore, time.Now()) + s.TestT[testName] = t } -func (suite *SuiteTester) AfterTest(suiteName, testName string) { - suite.SuiteNameAfter = append(suite.SuiteNameAfter, suiteName) - suite.TestNameAfter = append(suite.TestNameAfter, testName) - suite.TimeAfter = append(suite.TimeAfter, time.Now()) +func (s *SuiteTester) AfterTest(t *suite.T, suiteName, testName string) { + s.SuiteNameAfter = append(s.SuiteNameAfter, suiteName) + s.TestNameAfter = append(s.TestNameAfter, testName) + s.TimeAfter = append(s.TimeAfter, time.Now()) // T should be from the sub-test - assert.True(suite.T(), suite.TestT[testName] == suite.T()) + assert.True(t, s.TestT[testName] == t) } // The TearDownSuite method will be run by testify once, at the very // end of the testing suite, after all tests have been run. -func (suite *SuiteTester) TearDownSuite() { - suite.TearDownSuiteRunCount++ +func (s *SuiteTester) TearDownSuite(t *suite.T) { + s.TearDownSuiteRunCount++ // T should be from the suite - assert.True(suite.T(), suite.SuiteT == suite.T()) + assert.True(t, s.SuiteT == t) } // The SetupTest method will be run before every test in the suite. -func (suite *SuiteTester) SetupTest() { - suite.SetupTestRunCount++ - var subSuites []*testing.T - for _, value := range suite.TestT { +func (s *SuiteTester) SetupTest(t *suite.T) { + s.SetupTestRunCount++ + var subSuites []*suite.T + for _, value := range s.TestT { subSuites = append(subSuites, value) } // T should be from one of the tests, not suite - assert.Contains(suite.T(), subSuites, suite.T()) - assert.False(suite.T(), suite.SuiteT == suite.T() || suite.SuiteGroupT == suite.T()) + assert.Contains(t, subSuites, t) + assert.False(t, s.SuiteT == t) } // The TearDownTest method will be run after every test in the suite. -func (suite *SuiteTester) TearDownTest() { - suite.TearDownTestRunCount++ - var subSuites []*testing.T - for _, value := range suite.TestT { +func (s *SuiteTester) TearDownTest(t *suite.T) { + s.TearDownTestRunCount++ + var subSuites []*suite.T + for _, value := range s.TestT { subSuites = append(subSuites, value) } // T should be from one of the tests, not suite - assert.Contains(suite.T(), subSuites, suite.T()) - assert.False(suite.T(), suite.SuiteT == suite.T() || suite.SuiteGroupT == suite.T()) + assert.Contains(t, subSuites, t) + assert.False(t, s.SuiteT == t) } // Every method in a testing suite that begins with "Test" will be run // as a test. TestOne is an example of a test. For the purposes of // this example, we've included assertions in the tests, since most // tests will issue assertions. -func (suite *SuiteTester) TestOne() { - beforeCount := suite.TestOneRunCount - suite.TestOneRunCount++ - assert.Equal(suite.T(), suite.TestOneRunCount, beforeCount+1) - suite.Equal(suite.TestOneRunCount, beforeCount+1) +func (s *SuiteTester) TestOne(t *suite.T) { + beforeCount := s.TestOneRunCount + s.TestOneRunCount++ + assert.Equal(t, s.TestOneRunCount, beforeCount+1) + t.Equal(s.TestOneRunCount, beforeCount+1) // T should be from the right test - assert.True(suite.T(), suite.T() == suite.TestT["TestOne"]) + assert.True(t, t == s.TestT["TestOne"]) } // TestTwo is another example of a test. -func (suite *SuiteTester) TestTwo() { - beforeCount := suite.TestTwoRunCount - suite.TestTwoRunCount++ - assert.NotEqual(suite.T(), suite.TestTwoRunCount, beforeCount) - suite.NotEqual(suite.TestTwoRunCount, beforeCount) +func (s *SuiteTester) TestTwo(t *suite.T) { + beforeCount := s.TestTwoRunCount + s.TestTwoRunCount++ + assert.NotEqual(t, s.TestTwoRunCount, beforeCount) + t.NotEqual(s.TestTwoRunCount, beforeCount) // T should be from the right test - assert.True(suite.T(), suite.T() == suite.TestT["TestTwo"]) + assert.True(t, t == s.TestT["TestTwo"]) } -func (suite *SuiteTester) TestSkip() { - suite.T().Skip() +func (s *SuiteTester) TestSkip(t *suite.T) { + t.Skip() // T should be from the right test - assert.True(suite.T(), suite.T() == suite.TestT["TestSkip"]) + assert.True(t, t == s.TestT["TestSkip"]) } // NonTestMethod does not begin with "Test", so it will not be run by // testify as a test in the suite. This is useful for creating helper // methods for your tests. -func (suite *SuiteTester) NonTestMethod() { - suite.NonTestMethodRunCount++ +func (s *SuiteTester) NonTestMethod() { + s.NonTestMethodRunCount++ } -func (suite *SuiteTester) TestSubtest() { - suite.TestSubtestRunCount++ +func (s *SuiteTester) TestSubtest(t *suite.T) { + s.TestSubtestRunCount++ // T should be from the right test - assert.True(suite.T(), suite.T() == suite.TestT["TestSubtest"]) + assert.True(t, t == s.TestT["TestSubtest"]) - for _, t := range []struct { + for _, spec := range []struct { testName string }{ {"first"}, {"second"}, } { - suiteT := suite.T() - suite.Run(t.testName, func() { + suiteT := t + t.Run(spec.testName, func(t *suite.T) { // We should get a different *testing.T for subtests, so that // go test recognizes them as proper subtests for output formatting // and running individual subtests - subTestT := suite.T() - suite.NotEqual(subTestT, suiteT) + subTestT := t + t.NotEqual(subTestT, suiteT) }) - suite.Equal(suiteT, suite.T()) } } type SuiteSkipTester struct { - // Include our basic suite logic. - Suite - // Keep counts of how many times each method is run. SetupSuiteRunCount int TearDownSuiteRunCount int } -func (suite *SuiteSkipTester) SetupSuite() { - suite.SetupSuiteRunCount++ - suite.T().Skip() +func (s *SuiteSkipTester) SetupSuite(t *suite.T) { + s.SetupSuiteRunCount++ + t.Skip() } -func (suite *SuiteSkipTester) TestNothing() { +func (s *SuiteSkipTester) TestNothing(t *suite.T) { // SetupSuite is only called when at least one test satisfies // test filter. For this suite to be set up (and then tore down) // it is necessary to add at least one test method. } -func (suite *SuiteSkipTester) TearDownSuite() { - suite.TearDownSuiteRunCount++ +func (s *SuiteSkipTester) TearDownSuite(t *suite.T) { + s.TearDownSuiteRunCount++ } // TestRunSuite will be run by the 'go test' command, so within it, we // can run our suite using the Run(*testing.T, TestingSuite) function. func TestRunSuite(t *testing.T) { suiteTester := &SuiteTester{ - TestT: make(map[string]*testing.T), - SuiteT: t, + TestT: make(map[string]*suite.T), + //SuiteT: t, } - Run(t, suiteTester) + suite.Run(t, suiteTester) // Normally, the test would end here. The following are simply // some assertions to ensure that the Run function is working as @@ -379,7 +371,7 @@ func TestRunSuite(t *testing.T) { assert.Equal(t, suiteTester.NonTestMethodRunCount, 0) suiteSkipTester := new(SuiteSkipTester) - Run(t, suiteSkipTester) + suite.Run(t, suiteSkipTester) // The suite was only run once, so SetupSuite method should have been // run only once. Since SetupSuite called Skip(), Teardown isn't called. @@ -390,8 +382,6 @@ func TestRunSuite(t *testing.T) { // This suite has no Test... methods. It's setup and teardown must be skipped. type SuiteSetupSkipTester struct { - Suite - setUp bool toreDown bool } @@ -410,31 +400,20 @@ func (s *SuiteSetupSkipTester) TearDownSuite() { func TestSkippingSuiteSetup(t *testing.T) { suiteTester := new(SuiteSetupSkipTester) - Run(t, suiteTester) + suite.Run(t, suiteTester) assert.False(t, suiteTester.setUp) assert.False(t, suiteTester.toreDown) } -func TestSuiteGetters(t *testing.T) { - suite := new(SuiteTester) - suite.setT(t) - assert.NotNil(t, suite.Assert()) - assert.Equal(t, suite.Assertions, suite.Assert()) - assert.NotNil(t, suite.Require()) - assert.Equal(t, suite.require, suite.Require()) -} +type SuiteLoggingTester struct{} -type SuiteLoggingTester struct { - Suite +func (s *SuiteLoggingTester) TestLoggingPass(t *suite.T) { + t.Log("TESTLOGPASS") } -func (s *SuiteLoggingTester) TestLoggingPass() { - s.T().Log("TESTLOGPASS") -} - -func (s *SuiteLoggingTester) TestLoggingFail() { - s.T().Log("TESTLOGFAIL") - assert.NotNil(s.T(), nil) // expected to fail +func (s *SuiteLoggingTester) TestLoggingFail(t *suite.T) { + t.Log("TESTLOGFAIL") + assert.NotNil(t, nil) // expected to fail } type StdoutCapture struct { @@ -466,7 +445,7 @@ func TestSuiteLogging(t *testing.T) { internalTest := testing.InternalTest{ Name: "SomeTest", F: func(subT *testing.T) { - Run(subT, suiteLoggingTester) + suite.Run(subT, suiteLoggingTester) }, } capture.StartCapture() @@ -487,7 +466,6 @@ func TestSuiteLogging(t *testing.T) { } type CallOrderSuite struct { - Suite callOrder []string } @@ -497,51 +475,50 @@ func (s *CallOrderSuite) call(method string) { } func TestSuiteCallOrder(t *testing.T) { - Run(t, new(CallOrderSuite)) + suite.Run(t, new(CallOrderSuite)) } -func (s *CallOrderSuite) SetupSuite() { +func (s *CallOrderSuite) SetupSuite(t *suite.T) { s.call("SetupSuite") } -func (s *CallOrderSuite) TearDownSuite() { +func (s *CallOrderSuite) TearDownSuite(t *suite.T) { s.call("TearDownSuite") - assert.Equal(s.T(), "SetupSuite;SetupTest;Test A;TearDownTest;SetupTest;Test B;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) + assert.Equal(t, "SetupSuite;SetupTest;Test A;TearDownTest;SetupTest;Test B;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) } -func (s *CallOrderSuite) SetupTest() { +func (s *CallOrderSuite) SetupTest(t *suite.T) { s.call("SetupTest") } -func (s *CallOrderSuite) TearDownTest() { +func (s *CallOrderSuite) TearDownTest(t *suite.T) { s.call("TearDownTest") } -func (s *CallOrderSuite) Test_A() { +func (s *CallOrderSuite) Test_A(t *suite.T) { s.call("Test A") } -func (s *CallOrderSuite) Test_B() { +func (s *CallOrderSuite) Test_B(t *suite.T) { s.call("Test B") } type suiteWithStats struct { - Suite wasCalled bool - stats *SuiteInformation + stats *suite.SuiteInformation } -func (s *suiteWithStats) HandleStats(suiteName string, stats *SuiteInformation) { +func (s *suiteWithStats) HandleStats(t *suite.T, suiteName string, stats *suite.SuiteInformation) { s.wasCalled = true s.stats = stats } -func (s *suiteWithStats) TestSomething() { - s.Equal(1, 1) +func (s *suiteWithStats) TestSomething(t *suite.T) { + t.Equal(1, 1) } func TestSuiteWithStats(t *testing.T) { suiteWithStats := new(suiteWithStats) - Run(t, suiteWithStats) + suite.Run(t, suiteWithStats) assert.True(t, suiteWithStats.wasCalled) assert.NotZero(t, suiteWithStats.stats.Start) @@ -557,7 +534,6 @@ func TestSuiteWithStats(t *testing.T) { // FailfastSuite will test the behavior when running with the failfast flag // It logs calls in the callOrder slice which we then use to assert the correct calls were made type FailfastSuite struct { - Suite callOrder []string } @@ -575,7 +551,7 @@ func TestFailfastSuite(t *testing.T) { []testing.InternalTest{{ Name: "TestFailfastSuite", F: func(t *testing.T) { - Run(t, s) + suite.Run(t, s) }, }}, ) @@ -601,39 +577,38 @@ func TestFailfastSuiteFailFastOn(t *testing.T) { t.Fail() } } -func (s *FailfastSuite) SetupSuite() { +func (s *FailfastSuite) SetupSuite(t *suite.T) { s.call("SetupSuite") } -func (s *FailfastSuite) TearDownSuite() { +func (s *FailfastSuite) TearDownSuite(t *suite.T) { s.call("TearDownSuite") } -func (s *FailfastSuite) SetupTest() { +func (s *FailfastSuite) SetupTest(t *suite.T) { s.call("SetupTest") } -func (s *FailfastSuite) TearDownTest() { +func (s *FailfastSuite) TearDownTest(t *suite.T) { s.call("TearDownTest") } -func (s *FailfastSuite) Test_A_Fails() { +func (s *FailfastSuite) Test_A_Fails(t *suite.T) { s.call("Test A Fails") - s.T().Error("Test A meant to fail") + t.Error("Test A meant to fail") } -func (s *FailfastSuite) Test_B_Passes() { +func (s *FailfastSuite) Test_B_Passes(t *suite.T) { s.call("Test B Passes") - s.Require().True(true) + t.Require().True(true) } type parallelSuiteData struct { calls []string callsIndex map[string]int - parallelSuiteT map[string]*parallelSuite + parallelSuiteT map[string]*suite.T } type parallelSuite struct { - Suite mutex *sync.Mutex data *parallelSuiteData } @@ -646,65 +621,59 @@ func (s *parallelSuite) call(method string) { s.data.callsIndex[method] = len(s.data.calls) - 1 } -func (s *parallelSuite) Copy() TestingSuite { - // shallow copy since data is protected by mutex anyway - c := *s - return &c -} - func TestSuiteParallel(t *testing.T) { data := parallelSuiteData{ calls: []string{}, callsIndex: make(map[string]int, 8), - parallelSuiteT: make(map[string]*parallelSuite, 2), + parallelSuiteT: make(map[string]*suite.T, 2), } s := ¶llelSuite{mutex: &sync.Mutex{}, data: &data} - Run(t, s) + suite.Run(t, s) } -func (s *parallelSuite) SetupSuite() { +func (s *parallelSuite) SetupSuite(t *suite.T) { s.call("SetupSuite") } -func (s *parallelSuite) TearDownSuite() { +func (s *parallelSuite) TearDownSuite(t *suite.T) { s.call("TearDownSuite") s.mutex.Lock() defer s.mutex.Unlock() // first 3 calls and last call is known ordering - assert.Equal(s.T(), []string{"SetupSuite", "BeforeTest Test_A", "BeforeTest Test_B"}, s.data.calls[:3]) - assert.Equal(s.T(), "TearDownSuite", s.data.calls[len(s.data.calls)-1]) + assert.Equal(t, []string{"SetupSuite", "BeforeTest Test_A", "BeforeTest Test_B"}, s.data.calls[:3]) + assert.Equal(t, "TearDownSuite", s.data.calls[len(s.data.calls)-1]) // should have these calls - assert.Subset(s.T(), s.data.calls, []string{"Test_A", "AfterTest Test_A", "Test_B", "AfterTest Test_B"}) + assert.Subset(t, s.data.calls[3:], []string{"Test_A", "AfterTest Test_A", "Test_B", "AfterTest Test_B"}) // there won't be any other ordering guarantees between tests A and B since they are run in parallel, // but verify that AfterTest is run after the test - assert.Greater(s.T(), s.data.callsIndex["AfterTest Test_A"], s.data.callsIndex["Test_A"]) - assert.Greater(s.T(), s.data.callsIndex["AfterTest Test_B"], s.data.callsIndex["Test_B"]) + assert.Greater(t, s.data.callsIndex["AfterTest Test_A"], s.data.callsIndex["Test_A"]) + assert.Greater(t, s.data.callsIndex["AfterTest Test_B"], s.data.callsIndex["Test_B"]) // verify that copies of s are created correctly - assert.NotEqual(s.T(), s, s.data.parallelSuiteT["Test_A"]) - assert.NotEqual(s.T(), s, s.data.parallelSuiteT["Test_B"]) - assert.NotEqual(s.T(), s.data.parallelSuiteT["Test_A"], s.data.parallelSuiteT["Test_B"]) + assert.NotEqual(t, s, s.data.parallelSuiteT["Test_A"]) + assert.NotEqual(t, s, s.data.parallelSuiteT["Test_B"]) + assert.NotEqual(t, s.data.parallelSuiteT["Test_A"], s.data.parallelSuiteT["Test_B"]) } -func (s *parallelSuite) BeforeTest(suiteName, testName string) { +func (s *parallelSuite) BeforeTest(t *suite.T, suiteName, testName string) { s.call(fmt.Sprintf("BeforeTest %s", testName)) } -func (s *parallelSuite) AfterTest(suiteName, testName string) { +func (s *parallelSuite) AfterTest(t *suite.T, suiteName, testName string) { s.call(fmt.Sprintf("AfterTest %s", testName)) } -func (s *parallelSuite) Test_A() { - s.T().Parallel() +func (s *parallelSuite) Test_A(t *suite.T) { + t.Parallel() s.call("Test_A") s.mutex.Lock() defer s.mutex.Unlock() - s.data.parallelSuiteT["Test_A"] = s + s.data.parallelSuiteT["Test_A"] = t } -func (s *parallelSuite) Test_B() { - s.T().Parallel() +func (s *parallelSuite) Test_B(t *suite.T) { + t.Parallel() s.call("Test_B") s.mutex.Lock() defer s.mutex.Unlock() - s.data.parallelSuiteT["Test_B"] = s + s.data.parallelSuiteT["Test_B"] = t }