Repository: stretchr/testify
Pull request #909 (2020-03-03 19:54:03)

Add stats to suites and tests
Hello! After the feedback I got in [my previous PR](https://github.com/stretchr/testify/pull/900), here comes a new one with the suggestions you asked.

In this new PR, if a suite has a callback method called `HandleStats(suiteName string, stats *suite.SuiteInformation)`, `suite.Run` will collect stats for the suite under execution that can be used later to measure test speed, flaky tests etc.

I wrote [an example](https://github.com/esdrasbeleza/testify-stats/blob/master/example/sample_test.go#L20-L33) which creates a log file with information for every test execution. In real life, we could use not only log files, but store `SuiteInformation` in a database for posterior analysis.

I'm not really sure about the names I used for my fields and functions, feedback is always welcome. 😄


Patch:
diff --git a/assert/assertion_order.go b/assert/assertion_compare.go
similarity index 62%
rename from assert/assertion_order.go
rename to assert/assertion_compare.go
index 15a486c..66961ba 100644
--- a/assert/assertion_order.go
+++ b/assert/assertion_compare.go
@@ -5,20 +5,28 @@ import (
 	"reflect"
 )
 
-func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
+type CompareType int
+
+const (
+	compareLess CompareType = iota - 1
+	compareEqual
+	compareGreater
+)
+
+func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
 	switch kind {
 	case reflect.Int:
 		{
 			intobj1 := obj1.(int)
 			intobj2 := obj2.(int)
 			if intobj1 > intobj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if intobj1 == intobj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if intobj1 < intobj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.Int8:
@@ -26,13 +34,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			int8obj1 := obj1.(int8)
 			int8obj2 := obj2.(int8)
 			if int8obj1 > int8obj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if int8obj1 == int8obj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if int8obj1 < int8obj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.Int16:
@@ -40,13 +48,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			int16obj1 := obj1.(int16)
 			int16obj2 := obj2.(int16)
 			if int16obj1 > int16obj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if int16obj1 == int16obj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if int16obj1 < int16obj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.Int32:
@@ -54,13 +62,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			int32obj1 := obj1.(int32)
 			int32obj2 := obj2.(int32)
 			if int32obj1 > int32obj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if int32obj1 == int32obj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if int32obj1 < int32obj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.Int64:
@@ -68,13 +76,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			int64obj1 := obj1.(int64)
 			int64obj2 := obj2.(int64)
 			if int64obj1 > int64obj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if int64obj1 == int64obj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if int64obj1 < int64obj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.Uint:
@@ -82,13 +90,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			uintobj1 := obj1.(uint)
 			uintobj2 := obj2.(uint)
 			if uintobj1 > uintobj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if uintobj1 == uintobj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if uintobj1 < uintobj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.Uint8:
@@ -96,13 +104,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			uint8obj1 := obj1.(uint8)
 			uint8obj2 := obj2.(uint8)
 			if uint8obj1 > uint8obj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if uint8obj1 == uint8obj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if uint8obj1 < uint8obj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.Uint16:
@@ -110,13 +118,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			uint16obj1 := obj1.(uint16)
 			uint16obj2 := obj2.(uint16)
 			if uint16obj1 > uint16obj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if uint16obj1 == uint16obj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if uint16obj1 < uint16obj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.Uint32:
@@ -124,13 +132,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			uint32obj1 := obj1.(uint32)
 			uint32obj2 := obj2.(uint32)
 			if uint32obj1 > uint32obj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if uint32obj1 == uint32obj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if uint32obj1 < uint32obj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.Uint64:
@@ -138,13 +146,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			uint64obj1 := obj1.(uint64)
 			uint64obj2 := obj2.(uint64)
 			if uint64obj1 > uint64obj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if uint64obj1 == uint64obj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if uint64obj1 < uint64obj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.Float32:
@@ -152,13 +160,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			float32obj1 := obj1.(float32)
 			float32obj2 := obj2.(float32)
 			if float32obj1 > float32obj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if float32obj1 == float32obj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if float32obj1 < float32obj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.Float64:
@@ -166,13 +174,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			float64obj1 := obj1.(float64)
 			float64obj2 := obj2.(float64)
 			if float64obj1 > float64obj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if float64obj1 == float64obj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if float64obj1 < float64obj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	case reflect.String:
@@ -180,18 +188,18 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 			stringobj1 := obj1.(string)
 			stringobj2 := obj2.(string)
 			if stringobj1 > stringobj2 {
-				return -1, true
+				return compareGreater, true
 			}
 			if stringobj1 == stringobj2 {
-				return 0, true
+				return compareEqual, true
 			}
 			if stringobj1 < stringobj2 {
-				return 1, true
+				return compareLess, true
 			}
 		}
 	}
 
-	return 0, false
+	return compareEqual, false
 }
 
 // Greater asserts that the first element is greater than the second
@@ -200,26 +208,7 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
 //    assert.Greater(t, float64(2), float64(1))
 //    assert.Greater(t, "b", "a")
 func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
-	if h, ok := t.(tHelper); ok {
-		h.Helper()
-	}
-
-	e1Kind := reflect.ValueOf(e1).Kind()
-	e2Kind := reflect.ValueOf(e2).Kind()
-	if e1Kind != e2Kind {
-		return Fail(t, "Elements should be the same type", msgAndArgs...)
-	}
-
-	res, isComparable := compare(e1, e2, e1Kind)
-	if !isComparable {
-		return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
-	}
-
-	if res != -1 {
-		return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...)
-	}
-
-	return true
+	return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%s\" is not greater than \"%s\"", msgAndArgs)
 }
 
 // GreaterOrEqual asserts that the first element is greater than or equal to the second
@@ -229,26 +218,7 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface
 //    assert.GreaterOrEqual(t, "b", "a")
 //    assert.GreaterOrEqual(t, "b", "b")
 func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
-	if h, ok := t.(tHelper); ok {
-		h.Helper()
-	}
-
-	e1Kind := reflect.ValueOf(e1).Kind()
-	e2Kind := reflect.ValueOf(e2).Kind()
-	if e1Kind != e2Kind {
-		return Fail(t, "Elements should be the same type", msgAndArgs...)
-	}
-
-	res, isComparable := compare(e1, e2, e1Kind)
-	if !isComparable {
-		return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
-	}
-
-	if res != -1 && res != 0 {
-		return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...)
-	}
-
-	return true
+	return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%s\" is not greater than or equal to \"%s\"", msgAndArgs)
 }
 
 // Less asserts that the first element is less than the second
@@ -257,26 +227,7 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in
 //    assert.Less(t, float64(1), float64(2))
 //    assert.Less(t, "a", "b")
 func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
-	if h, ok := t.(tHelper); ok {
-		h.Helper()
-	}
-
-	e1Kind := reflect.ValueOf(e1).Kind()
-	e2Kind := reflect.ValueOf(e2).Kind()
-	if e1Kind != e2Kind {
-		return Fail(t, "Elements should be the same type", msgAndArgs...)
-	}
-
-	res, isComparable := compare(e1, e2, e1Kind)
-	if !isComparable {
-		return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
-	}
-
-	if res != 1 {
-		return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...)
-	}
-
-	return true
+	return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%s\" is not less than \"%s\"", msgAndArgs)
 }
 
 // LessOrEqual asserts that the first element is less than or equal to the second
@@ -286,6 +237,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{})
 //    assert.LessOrEqual(t, "a", "b")
 //    assert.LessOrEqual(t, "b", "b")
 func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
+	return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%s\" is not less than or equal to \"%s\"", msgAndArgs)
+}
+
+func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
 	}
@@ -296,14 +251,24 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
 		return Fail(t, "Elements should be the same type", msgAndArgs...)
 	}
 
-	res, isComparable := compare(e1, e2, e1Kind)
+	compareResult, isComparable := compare(e1, e2, e1Kind)
 	if !isComparable {
 		return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
 	}
 
-	if res != 1 && res != 0 {
-		return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...)
+	if !containsValue(allowedComparesResults, compareResult) {
+		return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...)
 	}
 
 	return true
 }
+
+func containsValue(values []CompareType, value CompareType) bool {
+	for _, v := range values {
+		if v == value {
+			return true
+		}
+	}
+
+	return false
+}
diff --git a/assert/assertions.go b/assert/assertions.go
index e0ea9b6..7bedb20 100644
--- a/assert/assertions.go
+++ b/assert/assertions.go
@@ -104,11 +104,11 @@ the problem actually occurred in calling code.*/
 // failed.
 func CallerInfo() []string {
 
-	pc := uintptr(0)
-	file := ""
-	line := 0
-	ok := false
-	name := ""
+	var pc uintptr
+	var ok bool
+	var file string
+	var line int
+	var name string
 
 	callers := []string{}
 	for i := 0; ; i++ {
@@ -661,7 +661,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
 		h.Helper()
 	}
 
-	if value != true {
+	if !value {
 		return Fail(t, "Should be true", msgAndArgs...)
 	}
 
@@ -677,7 +677,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
 		h.Helper()
 	}
 
-	if value != false {
+	if value {
 		return Fail(t, "Should be false", msgAndArgs...)
 	}
 
@@ -760,10 +760,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
 
 	ok, found := includeElement(s, contains)
 	if !ok {
-		return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
+		return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
 	}
 	if !found {
-		return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...)
+		return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...)
 	}
 
 	return true
@@ -1092,7 +1092,7 @@ func toFloat(x interface{}) (float64, bool) {
 	case float32:
 		xf = float64(xn)
 	case float64:
-		xf = float64(xn)
+		xf = xn
 	case time.Duration:
 		xf = float64(xn)
 	default:
diff --git a/mock/mock.go b/mock/mock.go
index 6be8937..3c0dc94 100644
--- a/mock/mock.go
+++ b/mock/mock.go
@@ -830,7 +830,7 @@ func (args Arguments) String(indexOrNil ...int) string {
 		// normal String() method - return a string representation of the args
 		var argsStr []string
 		for _, arg := range args {
-			argsStr = append(argsStr, fmt.Sprintf("%s", reflect.TypeOf(arg)))
+			argsStr = append(argsStr, reflect.TypeOf(arg).String())
 		}
 		return strings.Join(argsStr, ",")
 	} else if len(indexOrNil) == 1 {
diff --git a/suite/interfaces.go b/suite/interfaces.go
index b37cb04..8b98a8a 100644
--- a/suite/interfaces.go
+++ b/suite/interfaces.go
@@ -44,3 +44,10 @@ type BeforeTest interface {
 type AfterTest interface {
 	AfterTest(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)
+}
diff --git a/suite/stats.go b/suite/stats.go
new file mode 100644
index 0000000..261da37
--- /dev/null
+++ b/suite/stats.go
@@ -0,0 +1,46 @@
+package suite
+
+import "time"
+
+// SuiteInformation stats stores stats for the whole suite execution.
+type SuiteInformation struct {
+	Start, End time.Time
+	TestStats  map[string]*TestInformation
+}
+
+// TestInformation stores information about the execution of each test.
+type TestInformation struct {
+	TestName   string
+	Start, End time.Time
+	Passed     bool
+}
+
+func newSuiteInformation() *SuiteInformation {
+	testStats := make(map[string]*TestInformation)
+
+	return &SuiteInformation{
+		TestStats: testStats,
+	}
+}
+
+func (s SuiteInformation) start(testName string) {
+	s.TestStats[testName] = &TestInformation{
+		TestName: testName,
+		Start:    time.Now(),
+	}
+}
+
+func (s SuiteInformation) end(testName string, passed bool) {
+	s.TestStats[testName].End = time.Now()
+	s.TestStats[testName].Passed = passed
+}
+
+func (s SuiteInformation) Passed() bool {
+	for _, stats := range s.TestStats {
+		if !stats.Passed {
+			return false
+		}
+	}
+
+	return true
+}
diff --git a/suite/suite.go b/suite/suite.go
index 6195301..cdaf9a9 100644
--- a/suite/suite.go
+++ b/suite/suite.go
@@ -9,6 +9,7 @@ import (
 	"runtime/debug"
 	"sync"
 	"testing"
+	"time"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -87,6 +88,12 @@ func Run(t *testing.T, suite TestingSuite) {
 
 	suiteSetupDone := false
 
+	var stats *SuiteInformation
+
+	if _, ok := suite.(WithStats); ok {
+		stats = newSuiteInformation()
+	}
+
 	methodFinder := reflect.TypeOf(suite)
 	tests := []testing.InternalTest{}
 	for index := 0; index < methodFinder.NumMethod(); index++ {
@@ -96,21 +103,36 @@ func Run(t *testing.T, suite TestingSuite) {
 			fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err)
 			os.Exit(1)
 		}
+
 		if !ok {
 			continue
 		}
+
+		suiteName := methodFinder.Elem().Name()
+
 		if !suiteSetupDone {
+			if stats != nil {
+				stats.Start = time.Now()
+			}
+
 			if setupAllSuite, ok := suite.(SetupAllSuite); ok {
 				setupAllSuite.SetupSuite()
 			}
+
 			defer func() {
 				if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
 					testsSync.Wait()
 					tearDownAllSuite.TearDownSuite()
 				}
+
+				if suiteWithStats, measureStats := suite.(WithStats); measureStats {
+					stats.End = time.Now()
+					suiteWithStats.HandleStats(suiteName, stats)
+				}
 			}()
 			suiteSetupDone = true
 		}
+
 		test := testing.InternalTest{
 			Name: method.Name,
 			F: func(t *testing.T) {
@@ -122,16 +144,29 @@ func Run(t *testing.T, suite TestingSuite) {
 				if setupTestSuite, ok := suite.(SetupTestSuite); ok {
 					setupTestSuite.SetupTest()
 				}
+
 				if beforeTestSuite, ok := suite.(BeforeTest); ok {
 					beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name)
 				}
+
+				if stats != nil {
+					stats.start(method.Name)
+				}
+
 				defer func() {
+					if stats != nil {
+						passed := !t.Failed()
+						stats.end(method.Name, passed)
+					}
+
 					if afterTestSuite, ok := suite.(AfterTest); ok {
-						afterTestSuite.AfterTest(methodFinder.Elem().Name(), method.Name)
+						afterTestSuite.AfterTest(suiteName, method.Name)
 					}
+
 					if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
 						tearDownTestSuite.TearDownTest()
 					}
+
 					suite.SetT(parentT)
 				}()
 				method.Func.Call([]reflect.Value{reflect.ValueOf(suite)})


Test patch:
diff --git a/assert/assertion_order_test.go b/assert/assertion_compare_test.go
similarity index 53%
rename from assert/assertion_order_test.go
rename to assert/assertion_compare_test.go
index 4dd897a..daf9bea 100644
--- a/assert/assertion_order_test.go
+++ b/assert/assertion_compare_test.go
@@ -29,7 +29,7 @@ func TestCompare(t *testing.T) {
 			t.Error("object should be comparable for type " + currCase.cType)
 		}
 
-		if resLess != 1 {
+		if resLess != compareLess {
 			t.Errorf("object less should be less than greater for type " + currCase.cType)
 		}
 
@@ -38,7 +38,7 @@ func TestCompare(t *testing.T) {
 			t.Error("object are comparable for type " + currCase.cType)
 		}
 
-		if resGreater != -1 {
+		if resGreater != compareGreater {
 			t.Errorf("object greater should be greater than less for type " + currCase.cType)
 		}
 
@@ -116,3 +116,76 @@ func TestLessOrEqual(t *testing.T) {
 		t.Error("Greater should return false")
 	}
 }
+
+func Test_compareTwoValuesDifferentValuesTypes(t *testing.T) {
+	mockT := new(testing.T)
+
+	for _, currCase := range []struct {
+		v1            interface{}
+		v2            interface{}
+		compareResult bool
+	}{
+		{v1: 123, v2: "abc"},
+		{v1: "abc", v2: 123456},
+		{v1: float64(12), v2: "123"},
+		{v1: "float(12)", v2: float64(1)},
+	} {
+		compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, "testFailMessage")
+		False(t, compareResult)
+	}
+}
+
+func Test_compareTwoValuesNotComparableValues(t *testing.T) {
+	mockT := new(testing.T)
+
+	type CompareStruct struct {
+	}
+
+	for _, currCase := range []struct {
+		v1 interface{}
+		v2 interface{}
+	}{
+		{v1: CompareStruct{}, v2: CompareStruct{}},
+		{v1: map[string]int{}, v2: map[string]int{}},
+		{v1: make([]int, 5, 5), v2: make([]int, 5, 5)},
+	} {
+		compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, "testFailMessage")
+		False(t, compareResult)
+	}
+}
+
+func Test_compareTwoValuesCorrectCompareResult(t *testing.T) {
+	mockT := new(testing.T)
+
+	for _, currCase := range []struct {
+		v1           interface{}
+		v2           interface{}
+		compareTypes []CompareType
+	}{
+		{v1: 1, v2: 2, compareTypes: []CompareType{compareLess}},
+		{v1: 1, v2: 2, compareTypes: []CompareType{compareLess, compareEqual}},
+		{v1: 2, v2: 2, compareTypes: []CompareType{compareGreater, compareEqual}},
+		{v1: 2, v2: 2, compareTypes: []CompareType{compareEqual}},
+		{v1: 2, v2: 1, compareTypes: []CompareType{compareEqual, compareGreater}},
+		{v1: 2, v2: 1, compareTypes: []CompareType{compareGreater}},
+	} {
+		compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, currCase.compareTypes, "testFailMessage")
+		True(t, compareResult)
+	}
+}
+
+func Test_containsValue(t *testing.T) {
+	for _, currCase := range []struct {
+		values []CompareType
+		value  CompareType
+		result bool
+	}{
+		{values: []CompareType{compareGreater}, value: compareGreater, result: true},
+		{values: []CompareType{compareGreater, compareLess}, value: compareGreater, result: true},
+		{values: []CompareType{compareGreater, compareLess}, value: compareLess, result: true},
+		{values: []CompareType{compareGreater, compareLess}, value: compareEqual, result: false},
+	} {
+		compareResult := containsValue(currCase.values, currCase.value)
+		Equal(t, currCase.result, compareResult)
+	}
+}
diff --git a/assert/assertions_test.go b/assert/assertions_test.go
index 29f89da..b5e73ea 100644
--- a/assert/assertions_test.go
+++ b/assert/assertions_test.go
@@ -74,11 +74,11 @@ var (
 		[1]interface{}{1},
 		[]interface{}{},
 		struct{ x int }{1},
-		(*interface{})(&i),
-		(func())(func() {}),
+		(&i),
+		(func() {}),
 		interface{}(1),
 		map[interface{}]interface{}{},
-		(chan interface{})(make(chan interface{})),
+		(make(chan interface{})),
 		(<-chan interface{})(make(chan interface{})),
 		(chan<- interface{})(make(chan interface{})),
 	}
@@ -592,6 +592,18 @@ func TestContains(t *testing.T) {
 	}
 }
 
+func TestContainsFailMessage(t *testing.T) {
+
+	mockT := new(mockTestingT)
+
+	Contains(mockT, "Hello World", errors.New("Hello"))
+	expectedFail := "\"Hello World\" does not contain &errors.errorString{s:\"Hello\"}"
+	actualFail := mockT.errorString()
+	if !strings.Contains(actualFail, expectedFail) {
+		t.Errorf("Contains failure should include %q but was %q", expectedFail, actualFail)
+	}
+}
+
 func TestNotContains(t *testing.T) {
 
 	mockT := new(testing.T)
@@ -918,9 +930,6 @@ func TestNoError(t *testing.T) {
 	// returning an empty error interface
 	err = func() error {
 		var err *customError
-		if err != nil {
-			t.Fatal("err should be nil here")
-		}
 		return err
 	}()
 
@@ -955,9 +964,6 @@ func TestError(t *testing.T) {
 	// returning an empty error interface
 	err = func() error {
 		var err *customError
-		if err != nil {
-			t.Fatal("err should be nil here")
-		}
 		return err
 	}()
 
@@ -1026,7 +1032,6 @@ func TestEmpty(t *testing.T) {
 	type TString string
 	type TStruct struct {
 		x int
-		s []int
 	}
 
 	True(t, Empty(mockT, ""), "Empty string is empty")
@@ -2205,7 +2210,7 @@ func TestEventuallyTrue(t *testing.T) {
 	state := 0
 	condition := func() bool {
 		defer func() {
-			state = state + 1
+			state += 1
 		}()
 		return state == 2
 	}
diff --git a/http/test_response_writer.go b/http/test_response_writer.go
index 5c3f813..300a63b 100644
--- a/http/test_response_writer.go
+++ b/http/test_response_writer.go
@@ -36,7 +36,7 @@ func (rw *TestResponseWriter) Write(bytes []byte) (int, error) {
 	}
 
 	// add these bytes to the output string
-	rw.Output = rw.Output + string(bytes)
+	rw.Output += string(bytes)
 
 	// return normal values
 	return 0, nil
diff --git a/http/test_round_tripper.go b/http/test_round_tripper.go
index b1e32f1..11a024e 100644
--- a/http/test_round_tripper.go
+++ b/http/test_round_tripper.go
@@ -1,8 +1,9 @@
 package http
 
 import (
-	"github.com/stretchr/testify/mock"
 	"net/http"
+
+	"github.com/stretchr/testify/mock"
 )
 
 // TestRoundTripper DEPRECATED USE net/http/httptest
diff --git a/mock/mock_test.go b/mock/mock_test.go
index bfa123f..fcbc4f6 100644
--- a/mock/mock_test.go
+++ b/mock/mock_test.go
@@ -1325,6 +1325,7 @@ func Test_Arguments_Diff_WithArgMatcher(t *testing.T) {
 	assert.Contains(t, diff, `(bool=false) not matched by func(int) bool`)
 
 	diff, count = args.Diff([]interface{}{"string", 123, false})
+	assert.Equal(t, 1, count)
 	assert.Contains(t, diff, `(int=123) matched by func(int) bool`)
 
 	diff, count = args.Diff([]interface{}{"string", 123, true})
diff --git a/suite/stats_test.go b/suite/stats_test.go
new file mode 100644
index 0000000..4446a6d
--- /dev/null
+++ b/suite/stats_test.go
@@ -0,0 +1,29 @@
+package suite
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestPassedReturnsTrueWhenAllTestsPass(t *testing.T) {
+	sinfo := newSuiteInformation()
+	sinfo.TestStats = map[string]*TestInformation{
+		"Test1": {TestName: "Test1", Passed: true},
+		"Test2": {TestName: "Test2", Passed: true},
+		"Test3": {TestName: "Test3", Passed: true},
+	}
+
+	assert.True(t, sinfo.Passed())
+}
+
+func TestPassedReturnsFalseWhenSomeTestFails(t *testing.T) {
+	sinfo := newSuiteInformation()
+	sinfo.TestStats = map[string]*TestInformation{
+		"Test1": {TestName: "Test1", Passed: true},
+		"Test2": {TestName: "Test2", Passed: false},
+		"Test3": {TestName: "Test3", Passed: true},
+	}
+
+	assert.False(t, sinfo.Passed())
+}
diff --git a/suite/suite_test.go b/suite/suite_test.go
index 5d757dd..8b698a0 100644
--- a/suite/suite_test.go
+++ b/suite/suite_test.go
@@ -482,3 +482,33 @@ func (s *CallOrderSuite) Test_A() {
 func (s *CallOrderSuite) Test_B() {
 	s.call("Test B")
 }
+
+type suiteWithStats struct {
+	Suite
+	wasCalled bool
+	stats     *SuiteInformation
+}
+
+func (s *suiteWithStats) HandleStats(suiteName string, stats *SuiteInformation) {
+	s.wasCalled = true
+	s.stats = stats
+}
+
+func (s *suiteWithStats) TestSomething() {
+	s.Equal(1, 1)
+}
+
+func TestSuiteWithStats(t *testing.T) {
+	suiteWithStats := new(suiteWithStats)
+	Run(t, suiteWithStats)
+
+	assert.True(t, suiteWithStats.wasCalled)
+	assert.NotZero(t, suiteWithStats.stats.Start)
+	assert.NotZero(t, suiteWithStats.stats.End)
+	assert.True(t, suiteWithStats.stats.Passed())
+
+	testStats := suiteWithStats.stats.TestStats["TestSomething"]
+	assert.NotZero(t, testStats.Start)
+	assert.NotZero(t, testStats.End)
+	assert.True(t, testStats.Passed)
+}