Repository: stretchr/testify
Pull request #867 (2020-01-11 09:02:28)

changed dependency to interface
I encountered a problem when consuming testify through gobuffalo. I wanted to boot the development environment using a tests fixture from their fixture tooling. They use testify suites to integrate fixtures into controller tests. Since setting up a concrete T is quite laborious given much of what go test uses to do it is hidden, it makes more sense to me to provide a stub which can only work if testify consumes an interface.

glesica: I don’t think we want to do this because there could be methods added to ‘testing.T’ in the future. It also makes the signature different than what people might normally expect to see. Anyone else have thoughts?
npxcomplete: It's worth acknowledging that "this" is idiomatic in go, and is already done in both the require and assert packages. Perhaps you could outline what makes this case special in that it should accept a concrete type?
glesica: If we've done it in the past and no one has complained, then sure. I was just concerned that the change would fix your thing but break someone else's thing. Let's give it until Tuesday or so to give people time to share their thoughts and then I'll go ahead and merge it. Thanks!

Patch:
diff --git a/.travis.gofmt.sh b/.travis.gofmt.sh
index bfffdca..3f2e5d3 100755
--- a/.travis.gofmt.sh
+++ b/.travis.gofmt.sh
@@ -5,3 +5,10 @@ if [ -n "$(gofmt -l .)" ]; then
   gofmt -d .
   exit 1
 fi
+
+go generate ./...
+if [ -n "$(git status -s -uno)" ]; then
+  echo "Go generate output does not match commit."
+  echo "Did you forget to run go generate ./... ?"
+  exit 1
+fi
diff --git a/.travis.yml b/.travis.yml
index d6eead9..2d4a445 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,7 +19,7 @@ matrix:
       env: GO111MODULE=off
     - go: "1.13.x"
       env: GO111MODULE=on
-    - go: tip
+    - go: master
 script:
   - ./.travis.gogenerate.sh
   - ./.travis.gofmt.sh
diff --git a/Gopkg.lock b/Gopkg.lock
deleted file mode 100644
index 294cda0..0000000
--- a/Gopkg.lock
+++ /dev/null
@@ -1,27 +0,0 @@
-# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
-
-
-[[projects]]
-  name = "github.com/davecgh/go-spew"
-  packages = ["spew"]
-  revision = "346938d642f2ec3594ed81d874461961cd0faa76"
-  version = "v1.1.0"
-
-[[projects]]
-  name = "github.com/pmezard/go-difflib"
-  packages = ["difflib"]
-  revision = "792786c7400a136282c1664665ae0a8db921c6c2"
-  version = "v1.0.0"
-
-[[projects]]
-  name = "github.com/stretchr/objx"
-  packages = ["."]
-  revision = "facf9a85c22f48d2f52f2380e4efce1768749a89"
-  version = "v0.1"
-
-[solve-meta]
-  analyzer-name = "dep"
-  analyzer-version = 1
-  inputs-digest = "448ddae4702c6aded2555faafd390c537789bb1c483f70b0431e6634f73f2090"
-  solver-name = "gps-cdcl"
-  solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
deleted file mode 100644
index a16374c..0000000
--- a/Gopkg.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[prune]
-  unused-packages = true
-  non-go = true
-  go-tests = true
-
-[[constraint]]
-  name = "github.com/davecgh/go-spew"
-  version = "~1.1.0"
-
-[[constraint]]
-  name = "github.com/pmezard/go-difflib"
-  version = "~1.0.0"
-
-[[constraint]]
-  name = "github.com/stretchr/objx"
-  version = "~0.1.0"
diff --git a/assert/assertion_format.go b/assert/assertion_format.go
index d3a4a74..bf89ecd 100644
--- a/assert/assertion_format.go
+++ b/assert/assertion_format.go
@@ -363,6 +363,17 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args .
 	return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
 }
 
+// Neverf asserts that the given condition doesn't satisfy in waitFor time,
+// periodically checking the target function each tick.
+//
+//    assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
+func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+	return Never(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
+}
+
 // Nilf asserts that the specified object is nil.
 //
 //    assert.Nilf(t, err, "error message %s", "formatted")
diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go
index 84dab78..75ecdca 100644
--- a/assert/assertion_forward.go
+++ b/assert/assertion_forward.go
@@ -715,6 +715,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i
 	return Lessf(a.t, e1, e2, msg, args...)
 }
 
+// Never asserts that the given condition doesn't satisfy in waitFor time,
+// periodically checking the target function each tick.
+//
+//    a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond)
+func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	return Never(a.t, condition, waitFor, tick, msgAndArgs...)
+}
+
+// Neverf asserts that the given condition doesn't satisfy in waitFor time,
+// periodically checking the target function each tick.
+//
+//    a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
+func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	return Neverf(a.t, condition, waitFor, tick, msg, args...)
+}
+
 // Nil asserts that the specified object is nil.
 //
 //    a.Nil(err)
diff --git a/assert/assertions.go b/assert/assertions.go
index 2ca22eb..bdd8138 100644
--- a/assert/assertions.go
+++ b/assert/assertions.go
@@ -432,9 +432,11 @@ func formatUnequalValues(expected, actual interface{}) (e string, a string) {
 		return fmt.Sprintf("%T(%#v)", expected, expected),
 			fmt.Sprintf("%T(%#v)", actual, actual)
 	}
-
-	return fmt.Sprintf("%#v", expected),
-		fmt.Sprintf("%#v", actual)
+	switch expected.(type) {
+	case time.Duration:
+		return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual)
+	}
+	return fmt.Sprintf("%#v", expected), fmt.Sprintf("%#v", actual)
 }
 
 // EqualValues asserts that two objects are equal or convertable to the same types
@@ -1577,7 +1579,7 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
 	for tick := ticker.C; ; {
 		select {
 		case <-timer.C:
-			return false
+			return Fail(t, "Condition never satisfied", msgAndArgs...)
 		case <-tick:
 			tick = nil
 			go func() { ch <- condition() }()
@@ -1589,3 +1591,36 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
 		}
 	}
 }
+
+// Never asserts that the given condition doesn't satisfy in waitFor time,
+// periodically checking the target function each tick.
+//
+//    assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond)
+func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+
+	ch := make(chan bool, 1)
+
+	timer := time.NewTimer(waitFor)
+	defer timer.Stop()
+
+	ticker := time.NewTicker(tick)
+	defer ticker.Stop()
+
+	for tick := ticker.C; ; {
+		select {
+		case <-timer.C:
+			return true
+		case <-tick:
+			tick = nil
+			go func() { ch <- condition() }()
+		case v := <-ch:
+			if v {
+				return Fail(t, "Condition satisfied", msgAndArgs...)
+			}
+			tick = ticker.C
+		}
+	}
+}
diff --git a/require/require.go b/require/require.go
index 50ea1aa..cf6c7b5 100644
--- a/require/require.go
+++ b/require/require.go
@@ -914,6 +914,34 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter
 	t.FailNow()
 }
 
+// Never asserts that the given condition doesn't satisfy in waitFor time,
+// periodically checking the target function each tick.
+//
+//    assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond)
+func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+	if assert.Never(t, condition, waitFor, tick, msgAndArgs...) {
+		return
+	}
+	t.FailNow()
+}
+
+// Neverf asserts that the given condition doesn't satisfy in waitFor time,
+// periodically checking the target function each tick.
+//
+//    assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
+func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+	if assert.Neverf(t, condition, waitFor, tick, msg, args...) {
+		return
+	}
+	t.FailNow()
+}
+
 // Nil asserts that the specified object is nil.
 //
 //    assert.Nil(t, err)
diff --git a/require/require_forward.go b/require/require_forward.go
index 0dd39ec..5aac226 100644
--- a/require/require_forward.go
+++ b/require/require_forward.go
@@ -716,6 +716,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i
 	Lessf(a.t, e1, e2, msg, args...)
 }
 
+// Never asserts that the given condition doesn't satisfy in waitFor time,
+// periodically checking the target function each tick.
+//
+//    a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond)
+func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	Never(a.t, condition, waitFor, tick, msgAndArgs...)
+}
+
+// Neverf asserts that the given condition doesn't satisfy in waitFor time,
+// periodically checking the target function each tick.
+//
+//    a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
+func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	Neverf(a.t, condition, waitFor, tick, msg, args...)
+}
+
 // Nil asserts that the specified object is nil.
 //
 //    a.Nil(err)
diff --git a/suite/interfaces.go b/suite/interfaces.go
index b37cb04..b55db43 100644
--- a/suite/interfaces.go
+++ b/suite/interfaces.go
@@ -1,12 +1,10 @@
 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)
+	T() TestingT
+	SetT(TestingT)
 }
 
 // SetupAllSuite has a SetupSuite method, which will run before the
diff --git a/suite/suite.go b/suite/suite.go
index 17df20f..6e54781 100644
--- a/suite/suite.go
+++ b/suite/suite.go
@@ -7,6 +7,7 @@ import (
 	"reflect"
 	"regexp"
 	"runtime/debug"
+	"sync"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -16,21 +17,29 @@ import (
 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")
 
+type TestingT interface {
+	Run(name string, f func(t *testing.T)) bool
+	Errorf(format string, args ...interface{})
+	FailNow()
+	Log(args ...interface{})
+	Skip(args ...interface{})
+}
+
 // Suite is a basic testing suite with methods for storing and
 // retrieving the current *testing.T context.
 type Suite struct {
 	*assert.Assertions
 	require *require.Assertions
-	t       *testing.T
+	t       TestingT
 }
 
 // T retrieves the current *testing.T context.
-func (suite *Suite) T() *testing.T {
+func (suite *Suite) T() TestingT {
 	return suite.t
 }
 
 // SetT sets the current *testing.T context.
-func (suite *Suite) SetT(t *testing.T) {
+func (suite *Suite) SetT(t TestingT) {
 	suite.t = t
 	suite.Assertions = assert.New(t)
 	suite.require = require.New(t)
@@ -80,6 +89,7 @@ func (suite *Suite) Run(name string, subtest func()) bool {
 // Run takes a testing suite and runs all of the tests attached
 // to it.
 func Run(t *testing.T, suite TestingSuite) {
+	testsSync := &sync.WaitGroup{}
 	suite.SetT(t)
 	defer failOnPanic(t)
 
@@ -103,6 +113,7 @@ func Run(t *testing.T, suite TestingSuite) {
 			}
 			defer func() {
 				if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
+					testsSync.Wait()
 					tearDownAllSuite.TearDownSuite()
 				}
 			}()
@@ -111,6 +122,7 @@ func Run(t *testing.T, suite TestingSuite) {
 		test := testing.InternalTest{
 			Name: method.Name,
 			F: func(t *testing.T) {
+				defer testsSync.Done()
 				parentT := suite.T()
 				suite.SetT(t)
 				defer failOnPanic(t)
@@ -134,6 +146,7 @@ func Run(t *testing.T, suite TestingSuite) {
 			},
 		}
 		tests = append(tests, test)
+		testsSync.Add(1)
 	}
 	runTests(t, tests)
 }


Test patch:
diff --git a/assert/assertions_test.go b/assert/assertions_test.go
index b851116..35a643e 100644
--- a/assert/assertions_test.go
+++ b/assert/assertions_test.go
@@ -1831,6 +1831,15 @@ Diff:
 	Equal(t, expected, actual)
 }
 
+func TestTimeEqualityErrorFormatting(t *testing.T) {
+	mockT := new(mockTestingT)
+
+	Equal(mockT, time.Second*2, time.Millisecond)
+
+	expectedErr := "\\s+Error Trace:\\s+Error:\\s+Not equal:\\s+\n\\s+expected: 2s\n\\s+actual\\s+: 1ms\n"
+	Regexp(t, regexp.MustCompile(expectedErr), mockT.errorString())
+}
+
 func TestDiffEmptyCases(t *testing.T) {
 	Equal(t, "", diff(nil, nil))
 	Equal(t, "", diff(struct{ foo string }{}, nil))
@@ -1875,9 +1884,18 @@ func TestDiffRace(t *testing.T) {
 }
 
 type mockTestingT struct {
+	errorFmt string
+	args     []interface{}
+}
+
+func (m *mockTestingT) errorString() string {
+	return fmt.Sprintf(m.errorFmt, m.args...)
 }
 
-func (m *mockTestingT) Errorf(format string, args ...interface{}) {}
+func (m *mockTestingT) Errorf(format string, args ...interface{}) {
+	m.errorFmt = format
+	m.args = args
+}
 
 func TestFailNowWithPlainTestingT(t *testing.T) {
 	mockT := &mockTestingT{}
@@ -2149,6 +2167,27 @@ func TestEventuallyTrue(t *testing.T) {
 	True(t, Eventually(t, condition, 100*time.Millisecond, 20*time.Millisecond))
 }
 
+func TestNeverFalse(t *testing.T) {
+	condition := func() bool {
+		return false
+	}
+
+	True(t, Never(t, condition, 100*time.Millisecond, 20*time.Millisecond))
+}
+
+func TestNeverTrue(t *testing.T) {
+	mockT := new(testing.T)
+	state := 0
+	condition := func() bool {
+		defer func() {
+			state = state + 1
+		}()
+		return state == 2
+	}
+
+	False(t, Never(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
+}
+
 func TestEventuallyIssue805(t *testing.T) {
 	mockT := new(testing.T)
 
diff --git a/package_test.go b/package_test.go
index 7ac5d6d..f17ff15 100644
--- a/package_test.go
+++ b/package_test.go
@@ -1,8 +1,9 @@
 package testify
 
 import (
-	"github.com/stretchr/testify/assert"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestImports(t *testing.T) {
diff --git a/suite/suite_test.go b/suite/suite_test.go
index e08467d..5d757dd 100644
--- a/suite/suite_test.go
+++ b/suite/suite_test.go
@@ -3,7 +3,9 @@ package suite
 import (
 	"errors"
 	"io/ioutil"
+	"math/rand"
 	"os"
+	"strings"
 	"testing"
 	"time"
 
@@ -443,3 +445,40 @@ func TestSuiteLogging(t *testing.T) {
 		assert.NotContains(t, output, "TESTLOGPASS")
 	}
 }
+
+type CallOrderSuite struct {
+	Suite
+	callOrder []string
+}
+
+func (s *CallOrderSuite) call(method string) {
+	time.Sleep(time.Duration(rand.Intn(300)) * time.Millisecond)
+	s.callOrder = append(s.callOrder, method)
+}
+
+func TestSuiteCallOrder(t *testing.T) {
+	Run(t, new(CallOrderSuite))
+}
+func (s *CallOrderSuite) SetupSuite() {
+	s.call("SetupSuite")
+}
+
+func (s *CallOrderSuite) TearDownSuite() {
+	s.call("TearDownSuite")
+	assert.Equal(s.T(), "SetupSuite;SetupTest;Test A;TearDownTest;SetupTest;Test B;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";"))
+}
+func (s *CallOrderSuite) SetupTest() {
+	s.call("SetupTest")
+}
+
+func (s *CallOrderSuite) TearDownTest() {
+	s.call("TearDownTest")
+}
+
+func (s *CallOrderSuite) Test_A() {
+	s.call("Test A")
+}
+
+func (s *CallOrderSuite) Test_B() {
+	s.call("Test B")
+}