Repository: stretchr/testify
Pull request #706 (2018-12-23 18:39:55)

refactor includeElement method
The aim of the PR is just small refactoring:
 - Put calculation of `elementValue := reflect.ValueOf(element)` direct in place where it is required.
 - Calculate `listKind := reflect.TypeOf(list).Kind()` only one time.


Patch:
diff --git a/.travis.yml b/.travis.yml
index e5ed189..da6ba0d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,15 +2,18 @@ language: go
 
 sudo: false
 
-go:
-  - "1.8.x"
-  - "1.9.x"
-  - "1.10.x"
-  - "1.11.x"
-  - tip
-
-script:
-  - ./.travis.gogenerate.sh
-  - ./.travis.gofmt.sh
-  - ./.travis.govet.sh
-  - go test -v -race $(go list ./... | grep -v vendor)
+matrix:
+  include:
+    - go: "1.8.x"
+    - go: "1.9.x"
+    - go: "1.10.x"
+    - go: "1.11.x"
+      env: GO111MODULE=off
+    - go: "1.11.x"
+      env: GO111MODULE=on
+    - go: tip
+  script:
+    - ./.travis.gogenerate.sh
+    - ./.travis.gofmt.sh
+    - ./.travis.govet.sh
+    - go test -v -race $(go list ./... | grep -v vendor)
diff --git a/README.md b/README.md
index 0eb9987..11951d4 100644
--- a/README.md
+++ b/README.md
@@ -287,8 +287,10 @@ To install Testify, use `go get`:
 This will then make the following packages available to you:
 
     github.com/stretchr/testify/assert
+    github.com/stretchr/testify/require
     github.com/stretchr/testify/mock
-    github.com/stretchr/testify/http
+    github.com/stretchr/testify/suite
+    github.com/stretchr/testify/http (deprecated)
 
 Import the `testify/assert` package into your code using this template:
 
diff --git a/assert/assertions.go b/assert/assertions.go
index e8df681..cf1807e 100644
--- a/assert/assertions.go
+++ b/assert/assertions.go
@@ -419,6 +419,17 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
 	return Fail(t, "Expected value not to be nil.", msgAndArgs...)
 }
 
+// containsKind checks if a specified kind in the slice of kinds.
+func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool {
+	for i := 0; i < len(kinds); i++ {
+		if kind == kinds[i] {
+			return true
+		}
+	}
+
+	return false
+}
+
 // isNil checks if a specified object is nil or not, without Failing.
 func isNil(object interface{}) bool {
 	if object == nil {
@@ -427,7 +438,14 @@ func isNil(object interface{}) bool {
 
 	value := reflect.ValueOf(object)
 	kind := value.Kind()
-	if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
+	isNilableKind := containsKind(
+		[]reflect.Kind{
+			reflect.Chan, reflect.Func,
+			reflect.Interface, reflect.Map,
+			reflect.Ptr, reflect.Slice},
+		kind)
+
+	if isNilableKind && value.IsNil() {
 		return true
 	}
 
@@ -611,7 +629,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
 func includeElement(list interface{}, element interface{}) (ok, found bool) {
 
 	listValue := reflect.ValueOf(list)
-	elementValue := reflect.ValueOf(element)
+	listKind := reflect.TypeOf(list).Kind()
 	defer func() {
 		if e := recover(); e != nil {
 			ok = false
@@ -619,11 +637,12 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) {
 		}
 	}()
 
-	if reflect.TypeOf(list).Kind() == reflect.String {
+	if listKind == reflect.String {
+		elementValue := reflect.ValueOf(element)
 		return true, strings.Contains(listValue.String(), elementValue.String())
 	}
 
-	if reflect.TypeOf(list).Kind() == reflect.Map {
+	if listKind == reflect.Map {
 		mapKeys := listValue.MapKeys()
 		for i := 0; i < len(mapKeys); i++ {
 			if ObjectsAreEqual(mapKeys[i].Interface(), element) {
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..90e5dbe
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,7 @@
+module github.com/stretchr/testify
+
+require (
+	github.com/davecgh/go-spew v1.1.0
+	github.com/pmezard/go-difflib v1.0.0
+	github.com/stretchr/objx v0.1.0
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..5b98bf3
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,6 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
diff --git a/mock/mock.go b/mock/mock.go
index cc4f642..d6694ed 100644
--- a/mock/mock.go
+++ b/mock/mock.go
@@ -176,6 +176,7 @@ func (c *Call) Maybe() *Call {
 //    Mock.
 //       On("MyMethod", 1).Return(nil).
 //       On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
+//go:noinline
 func (c *Call) On(methodName string, arguments ...interface{}) *Call {
 	return c.Parent.On(methodName, arguments...)
 }
@@ -691,7 +692,7 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
 				output = fmt.Sprintf("%s\t%d: PASS:  %s matched by %s\n", output, i, actualFmt, matcher)
 			} else {
 				differences++
-				output = fmt.Sprintf("%s\t%d: PASS:  %s not matched by %s\n", output, i, actualFmt, matcher)
+				output = fmt.Sprintf("%s\t%d: FAIL:  %s not matched by %s\n", output, i, actualFmt, matcher)
 			}
 		} else if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() {
 
diff --git a/suite/suite.go b/suite/suite.go
index e20afbc..5cea8f8 100644
--- a/suite/suite.go
+++ b/suite/suite.go
@@ -55,10 +55,32 @@ func (suite *Suite) Assert() *assert.Assertions {
 	return suite.Assertions
 }
 
+func failOnPanic(t *testing.T) {
+	r := recover()
+	if r != nil {
+		t.Errorf("test panicked: %v", r)
+		t.FailNow()
+	}
+}
+
+// Run provides suite functionality around golang subtests.  It should be
+// 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.
+func (suite *Suite) Run(name string, subtest func()) bool {
+	oldT := suite.T()
+	defer suite.SetT(oldT)
+	return oldT.Run(name, func(t *testing.T) {
+		suite.SetT(t)
+		subtest()
+	})
+}
+
 // Run takes a testing suite and runs all of the tests attached
 // to it.
 func Run(t *testing.T, suite TestingSuite) {
 	suite.SetT(t)
+	defer failOnPanic(t)
 
 	if setupAllSuite, ok := suite.(SetupAllSuite); ok {
 		setupAllSuite.SetupSuite()
@@ -84,6 +106,8 @@ func Run(t *testing.T, suite TestingSuite) {
 				F: func(t *testing.T) {
 					parentT := suite.T()
 					suite.SetT(t)
+					defer failOnPanic(t)
+
 					if setupTestSuite, ok := suite.(SetupTestSuite); ok {
 						setupTestSuite.SetupTest()
 					}


Test patch:
diff --git a/mock/mock_test.go b/mock/mock_test.go
index 978eae2..2608f5a 100644
--- a/mock/mock_test.go
+++ b/mock/mock_test.go
@@ -32,6 +32,7 @@ func (i *TestExampleImplementation) TheExampleMethod(a, b, c int) (int, error) {
 	return args.Int(0), errors.New("Whoops")
 }
 
+//go:noinline
 func (i *TestExampleImplementation) TheExampleMethod2(yesorno bool) {
 	i.Called(yesorno)
 }
@@ -1492,6 +1493,7 @@ func unexpectedCallRegex(method, calledArg, expectedArg, diff string) string {
 		rMethod, calledArg, rMethod, expectedArg, diff)
 }
 
+//go:noinline
 func ConcurrencyTestMethod(m *Mock) {
 	m.Called()
 }
diff --git a/suite/suite_test.go b/suite/suite_test.go
index b75fa4a..6dfba08 100644
--- a/suite/suite_test.go
+++ b/suite/suite_test.go
@@ -42,6 +42,99 @@ func (s *SuiteRequireTwice) TestRequireTwo() {
 	r.Equal(1, 2)
 }
 
+type panickingSuite struct {
+	Suite
+	panicInSetupSuite    bool
+	panicInSetupTest     bool
+	panicInBeforeTest    bool
+	panicInTest          bool
+	panicInAfterTest     bool
+	panicInTearDownTest  bool
+	panicInTearDownSuite bool
+}
+
+func (s *panickingSuite) SetupSuite() {
+	if s.panicInSetupSuite {
+		panic("oops in setup suite")
+	}
+}
+
+func (s *panickingSuite) SetupTest() {
+	if s.panicInSetupTest {
+		panic("oops in setup test")
+	}
+}
+
+func (s *panickingSuite) BeforeTest(_, _ string) {
+	if s.panicInBeforeTest {
+		panic("oops in before test")
+	}
+}
+
+func (s *panickingSuite) Test() {
+	if s.panicInTest {
+		panic("oops in test")
+	}
+}
+
+func (s *panickingSuite) AfterTest(_, _ string) {
+	if s.panicInAfterTest {
+		panic("oops in after test")
+	}
+}
+
+func (s *panickingSuite) TearDownTest() {
+	if s.panicInTearDownTest {
+		panic("oops in tear down test")
+	}
+}
+
+func (s *panickingSuite) TearDownSuite() {
+	if s.panicInTearDownSuite {
+		panic("oops in tear down suite")
+	}
+}
+
+func TestSuiteRecoverPanic(t *testing.T) {
+	ok := true
+	panickingTests := []testing.InternalTest{
+		{
+			Name: "TestPanicInSetupSuite",
+			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInSetupSuite: true}) },
+		},
+		{
+			Name: "TestPanicInSetupTest",
+			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInSetupTest: true}) },
+		},
+		{
+			Name: "TestPanicInBeforeTest",
+			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInBeforeTest: true}) },
+		},
+		{
+			Name: "TestPanicInTest",
+			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTest: true}) },
+		},
+		{
+			Name: "TestPanicInAfterTest",
+			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInAfterTest: true}) },
+		},
+		{
+			Name: "TestPanicInTearDownTest",
+			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownTest: true}) },
+		},
+		{
+			Name: "TestPanicInTearDownSuite",
+			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownSuite: true}) },
+		},
+	}
+
+	require.NotPanics(t, func() {
+		ok = testing.RunTests(allTestsFilter, panickingTests)
+	})
+
+	assert.False(t, ok)
+}
+
 // This suite is intended to store values to make sure that only
 // testing-suite-related methods are run.  It's also a fully
 // functional example of a testing suite, using setup/teardown methods
@@ -59,6 +152,7 @@ type SuiteTester struct {
 	TearDownTestRunCount  int
 	TestOneRunCount       int
 	TestTwoRunCount       int
+	TestSubtestRunCount   int
 	NonTestMethodRunCount int
 
 	SuiteNameBefore []string
@@ -153,6 +247,27 @@ func (suite *SuiteTester) NonTestMethod() {
 	suite.NonTestMethodRunCount++
 }
 
+func (suite *SuiteTester) TestSubtest() {
+	suite.TestSubtestRunCount++
+
+	for _, t := range []struct {
+		testName string
+	}{
+		{"first"},
+		{"second"},
+	} {
+		suiteT := suite.T()
+		suite.Run(t.testName, func() {
+			// 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)
+		})
+		suite.Equal(suiteT, suite.T())
+	}
+}
+
 // 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) {
@@ -168,18 +283,20 @@ func TestRunSuite(t *testing.T) {
 	assert.Equal(t, suiteTester.SetupSuiteRunCount, 1)
 	assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1)
 
-	assert.Equal(t, len(suiteTester.SuiteNameAfter), 3)
-	assert.Equal(t, len(suiteTester.SuiteNameBefore), 3)
-	assert.Equal(t, len(suiteTester.TestNameAfter), 3)
-	assert.Equal(t, len(suiteTester.TestNameBefore), 3)
+	assert.Equal(t, len(suiteTester.SuiteNameAfter), 4)
+	assert.Equal(t, len(suiteTester.SuiteNameBefore), 4)
+	assert.Equal(t, len(suiteTester.TestNameAfter), 4)
+	assert.Equal(t, len(suiteTester.TestNameBefore), 4)
 
 	assert.Contains(t, suiteTester.TestNameAfter, "TestOne")
 	assert.Contains(t, suiteTester.TestNameAfter, "TestTwo")
 	assert.Contains(t, suiteTester.TestNameAfter, "TestSkip")
+	assert.Contains(t, suiteTester.TestNameAfter, "TestSubtest")
 
 	assert.Contains(t, suiteTester.TestNameBefore, "TestOne")
 	assert.Contains(t, suiteTester.TestNameBefore, "TestTwo")
 	assert.Contains(t, suiteTester.TestNameBefore, "TestSkip")
+	assert.Contains(t, suiteTester.TestNameBefore, "TestSubtest")
 
 	for _, suiteName := range suiteTester.SuiteNameAfter {
 		assert.Equal(t, "SuiteTester", suiteName)
@@ -197,15 +314,16 @@ func TestRunSuite(t *testing.T) {
 		assert.False(t, when.IsZero())
 	}
 
-	// There are three test methods (TestOne, TestTwo, and TestSkip), so
+	// There are four test methods (TestOne, TestTwo, TestSkip, and TestSubtest), so
 	// the SetupTest and TearDownTest methods (which should be run once for
-	// each test) should have been run three times.
-	assert.Equal(t, suiteTester.SetupTestRunCount, 3)
-	assert.Equal(t, suiteTester.TearDownTestRunCount, 3)
+	// each test) should have been run four times.
+	assert.Equal(t, suiteTester.SetupTestRunCount, 4)
+	assert.Equal(t, suiteTester.TearDownTestRunCount, 4)
 
 	// Each test should have been run once.
 	assert.Equal(t, suiteTester.TestOneRunCount, 1)
 	assert.Equal(t, suiteTester.TestTwoRunCount, 1)
+	assert.Equal(t, suiteTester.TestSubtestRunCount, 1)
 
 	// Methods that don't match the test method identifier shouldn't
 	// have been run at all.