diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 08796ae..be8b4af 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,8 +7,8 @@ jobs: strategy: matrix: go_version: - - "1.19" - "1.20" + - "1.21" steps: - uses: actions/checkout@v4 - name: Setup Go @@ -28,6 +28,7 @@ jobs: - "1.18" - "1.19" - "1.20" + - "1.21" steps: - uses: actions/checkout@v4 - name: Setup Go diff --git a/assert/assertion_format.go b/assert/assertion_format.go index 53f9c8e..896cc7f 100644 --- a/assert/assertion_format.go +++ b/assert/assertion_format.go @@ -657,10 +657,12 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") +// assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -744,10 +746,11 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg return Same(t, expected, actual, append([]interface{}{msg}, args...)...) } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") +// assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go index 5a2974b..8aaa259 100644 --- a/assert/assertion_forward.go +++ b/assert/assertion_forward.go @@ -1306,10 +1306,12 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri return NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2]) +// a.NotSubset({"x": 1, "y": 2}, {"z": 3}) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1317,10 +1319,12 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs return NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1480,10 +1484,11 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, return Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2]) +// a.Subset({"x": 1, "y": 2}, {"x": 1}) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1491,10 +1496,11 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... return Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/assert/assertions.go b/assert/assertions.go index 1e55fbf..909daa5 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -150,6 +150,8 @@ func copyExportedFields(expected interface{}) interface{} { // structures. // // This function does no assertion of any kind. +// +// Deprecated: Use [EqualExportedValues] instead. func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { expectedCleaned := copyExportedFields(expected) actualCleaned := copyExportedFields(actual) @@ -630,17 +632,6 @@ 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 { @@ -648,16 +639,13 @@ func isNil(object interface{}) bool { } value := reflect.ValueOf(object) - kind := value.Kind() - isNilableKind := containsKind( - []reflect.Kind{ - reflect.Chan, reflect.Func, - reflect.Interface, reflect.Map, - reflect.Ptr, reflect.Slice, reflect.UnsafePointer}, - kind) + switch value.Kind() { + case + reflect.Chan, reflect.Func, + reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice, reflect.UnsafePointer: - if isNilableKind && value.IsNil() { - return true + return value.IsNil() } return false @@ -927,10 +915,11 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2]) +// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -983,10 +972,12 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return true } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2]) +// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1466,19 +1457,26 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m if h, ok := t.(tHelper); ok { h.Helper() } - if expected == nil || actual == nil || - reflect.TypeOf(actual).Kind() != reflect.Slice || - reflect.TypeOf(expected).Kind() != reflect.Slice { + + if expected == nil || actual == nil { return Fail(t, "Parameters must be slice", msgAndArgs...) } - actualSlice := reflect.ValueOf(actual) expectedSlice := reflect.ValueOf(expected) + actualSlice := reflect.ValueOf(actual) - for i := 0; i < actualSlice.Len(); i++ { - result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) - if !result { - return result + if expectedSlice.Type().Kind() != reflect.Slice { + return Fail(t, "Expected value must be slice", msgAndArgs...) + } + + expectedLen := expectedSlice.Len() + if !IsType(t, expected, actual) || !Len(t, actual, expectedLen) { + return false + } + + for i := 0; i < expectedLen; i++ { + if !InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon, "at index %d", i) { + return false } } diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 75ee795..d65dd02 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -16,7 +16,6 @@ import ( "strings" "testing" "time" - "unsafe" ) var ( @@ -2922,52 +2921,211 @@ func Test_truncatingFormat(t *testing.T) { } } +// parseLabeledOutput does the inverse of labeledOutput - it takes a formatted +// output string and turns it back into a slice of labeledContent. +func parseLabeledOutput(output string) []labeledContent { + labelPattern := regexp.MustCompile(`^\t([^\t]*): *\t(.*)$`) + contentPattern := regexp.MustCompile(`^\t *\t(.*)$`) + var contents []labeledContent + lines := strings.Split(output, "\n") + i := -1 + for _, line := range lines { + if line == "" { + // skip blank lines + continue + } + matches := labelPattern.FindStringSubmatch(line) + if len(matches) == 3 { + // a label + contents = append(contents, labeledContent{ + label: matches[1], + content: matches[2] + "\n", + }) + i++ + continue + } + matches = contentPattern.FindStringSubmatch(line) + if len(matches) == 2 { + // just content + if i >= 0 { + contents[i].content += matches[1] + "\n" + continue + } + } + // Couldn't parse output + return nil + } + return contents +} + +type captureTestingT struct { + msg string +} + +func (ctt *captureTestingT) Errorf(format string, args ...interface{}) { + ctt.msg = fmt.Sprintf(format, args...) +} + +func (ctt *captureTestingT) checkResultAndErrMsg(t *testing.T, expectedRes, res bool, expectedErrMsg string) { + t.Helper() + if res != expectedRes { + t.Errorf("Should return %t", expectedRes) + return + } + contents := parseLabeledOutput(ctt.msg) + if res == true { + if contents != nil { + t.Errorf("Should not log an error") + } + return + } + if contents == nil { + t.Errorf("Should log an error. Log output: %v", ctt.msg) + return + } + for _, content := range contents { + if content.label == "Error" { + if expectedErrMsg == content.content { + return + } + t.Errorf("Logged Error: %v", content.content) + } + } + t.Errorf("Should log Error: %v", expectedErrMsg) +} + func TestErrorIs(t *testing.T) { - mockT := new(testing.T) tests := []struct { - err error - target error - result bool + err error + target error + result bool + resultErrMsg string }{ - {io.EOF, io.EOF, true}, - {fmt.Errorf("wrap: %w", io.EOF), io.EOF, true}, - {io.EOF, io.ErrClosedPipe, false}, - {nil, io.EOF, false}, - {io.EOF, nil, false}, - {nil, nil, true}, + { + err: io.EOF, + target: io.EOF, + result: true, + }, + { + err: fmt.Errorf("wrap: %w", io.EOF), + target: io.EOF, + result: true, + }, + { + err: io.EOF, + target: io.ErrClosedPipe, + result: false, + resultErrMsg: "" + + "Target error should be in err chain:\n" + + "expected: \"io: read/write on closed pipe\"\n" + + "in chain: \"EOF\"\n", + }, + { + err: nil, + target: io.EOF, + result: false, + resultErrMsg: "" + + "Target error should be in err chain:\n" + + "expected: \"EOF\"\n" + + "in chain: \n", + }, + { + err: io.EOF, + target: nil, + result: false, + resultErrMsg: "" + + "Target error should be in err chain:\n" + + "expected: \"\"\n" + + "in chain: \"EOF\"\n", + }, + { + err: nil, + target: nil, + result: true, + }, + { + err: fmt.Errorf("abc: %w", errors.New("def")), + target: io.EOF, + result: false, + resultErrMsg: "" + + "Target error should be in err chain:\n" + + "expected: \"EOF\"\n" + + "in chain: \"abc: def\"\n" + + "\t\"def\"\n", + }, } for _, tt := range tests { tt := tt t.Run(fmt.Sprintf("ErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { + mockT := new(captureTestingT) res := ErrorIs(mockT, tt.err, tt.target) - if res != tt.result { - t.Errorf("ErrorIs(%#v,%#v) should return %t", tt.err, tt.target, tt.result) - } + mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) }) } } func TestNotErrorIs(t *testing.T) { - mockT := new(testing.T) tests := []struct { - err error - target error - result bool + err error + target error + result bool + resultErrMsg string }{ - {io.EOF, io.EOF, false}, - {fmt.Errorf("wrap: %w", io.EOF), io.EOF, false}, - {io.EOF, io.ErrClosedPipe, true}, - {nil, io.EOF, true}, - {io.EOF, nil, true}, - {nil, nil, false}, + { + err: io.EOF, + target: io.EOF, + result: false, + resultErrMsg: "" + + "Target error should not be in err chain:\n" + + "found: \"EOF\"\n" + + "in chain: \"EOF\"\n", + }, + { + err: fmt.Errorf("wrap: %w", io.EOF), + target: io.EOF, + result: false, + resultErrMsg: "" + + "Target error should not be in err chain:\n" + + "found: \"EOF\"\n" + + "in chain: \"wrap: EOF\"\n" + + "\t\"EOF\"\n", + }, + { + err: io.EOF, + target: io.ErrClosedPipe, + result: true, + }, + { + err: nil, + target: io.EOF, + result: true, + }, + { + err: io.EOF, + target: nil, + result: true, + }, + { + err: nil, + target: nil, + result: false, + resultErrMsg: "" + + "Target error should not be in err chain:\n" + + "found: \"\"\n" + + "in chain: \n", + }, + { + err: fmt.Errorf("abc: %w", errors.New("def")), + target: io.EOF, + result: true, + }, } for _, tt := range tests { tt := tt t.Run(fmt.Sprintf("NotErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { + mockT := new(captureTestingT) res := NotErrorIs(mockT, tt.err, tt.target) - if res != tt.result { - t.Errorf("NotErrorIs(%#v,%#v) should return %t", tt.err, tt.target, tt.result) - } + mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg) }) } } @@ -2993,10 +3151,3 @@ func TestErrorAs(t *testing.T) { }) } } - -func TestIsNil(t *testing.T) { - var n unsafe.Pointer = nil - if !isNil(n) { - t.Fatal("fail") - } -} diff --git a/assert/internal/unsafetests/doc.go b/assert/internal/unsafetests/doc.go new file mode 100644 index 0000000..08172d5 --- /dev/null +++ b/assert/internal/unsafetests/doc.go @@ -0,0 +1,4 @@ +// This package exists just to isolate tests that reference the [unsafe] package. +// +// The tests in this package are totally safe. +package unsafetests diff --git a/assert/internal/unsafetests/unsafetests_test.go b/assert/internal/unsafetests/unsafetests_test.go new file mode 100644 index 0000000..b7f01a6 --- /dev/null +++ b/assert/internal/unsafetests/unsafetests_test.go @@ -0,0 +1,34 @@ +package unsafetests_test + +import ( + "fmt" + "testing" + "unsafe" + + "github.com/stretchr/testify/assert" +) + +type ignoreTestingT struct{} + +var _ assert.TestingT = ignoreTestingT{} + +func (ignoreTestingT) Helper() {} + +func (ignoreTestingT) Errorf(format string, args ...interface{}) { + // Run the formatting, but ignore the result + msg := fmt.Sprintf(format, args...) + _ = msg +} + +func TestUnsafePointers(t *testing.T) { + var ignore ignoreTestingT + + assert.True(t, assert.Nil(t, unsafe.Pointer(nil), "unsafe.Pointer(nil) is nil")) + assert.False(t, assert.NotNil(ignore, unsafe.Pointer(nil), "unsafe.Pointer(nil) is nil")) + + assert.True(t, assert.Nil(t, unsafe.Pointer((*int)(nil)), "unsafe.Pointer((*int)(nil)) is nil")) + assert.False(t, assert.NotNil(ignore, unsafe.Pointer((*int)(nil)), "unsafe.Pointer((*int)(nil)) is nil")) + + assert.False(t, assert.Nil(ignore, unsafe.Pointer(new(int)), "unsafe.Pointer(new(int)) is NOT nil")) + assert.True(t, assert.NotNil(t, unsafe.Pointer(new(int)), "unsafe.Pointer(new(int)) is NOT nil")) +} diff --git a/doc.go b/doc.go index 3460b46..aac5ef3 100644 --- a/doc.go +++ b/doc.go @@ -5,19 +5,7 @@ // // The assert package provides a comprehensive set of assertion functions that tie in to the Go testing system. // -// The http package contains tools to make it easier to test http activity using the Go testing system. -// // The mock package provides a system by which it is possible to mock your objects and verify calls are happening as expected. // // The suite package provides a basic structure for using structs as testing suites, and methods on those structs as tests. It includes setup/teardown functionality in the way of interfaces. package testify - -// blank imports help docs. -import ( - // assert package - _ "github.com/stretchr/testify/assert" - // http package - _ "github.com/stretchr/testify/http" - // mock package - _ "github.com/stretchr/testify/mock" -) diff --git a/go.mod b/go.mod index d3c4d72..9133c4a 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,10 @@ go 1.17 require ( github.com/davecgh/go-spew v1.1.1 github.com/pmezard/go-difflib v1.0.0 - github.com/stretchr/objx v0.5.0 + github.com/stretchr/objx v0.5.1 gopkg.in/yaml.v3 v3.0.1 ) + +// Break dependency cycle with objx. +// See https://github.com/stretchr/objx/pull/140 +exclude github.com/stretchr/testify v1.8.2 diff --git a/go.sum b/go.sum index 4f3ced6..f218355 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,10 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/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/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/http/doc.go b/http/doc.go index 695167c..7c800aa 100644 --- a/http/doc.go +++ b/http/doc.go @@ -1,2 +1,2 @@ -// Package http DEPRECATED USE net/http/httptest +// Deprecated: Use [net/http/httptest] instead. package http diff --git a/http/test_response_writer.go b/http/test_response_writer.go index 300a63b..6744e1c 100644 --- a/http/test_response_writer.go +++ b/http/test_response_writer.go @@ -4,7 +4,7 @@ import ( "net/http" ) -// TestResponseWriter DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +// Deprecated: Use [net/http/httptest] instead. type TestResponseWriter struct { // StatusCode is the last int written by the call to WriteHeader(int) @@ -17,7 +17,7 @@ type TestResponseWriter struct { header http.Header } -// Header DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +// Deprecated: Use [net/http/httptest] instead. func (rw *TestResponseWriter) Header() http.Header { if rw.header == nil { @@ -27,7 +27,7 @@ func (rw *TestResponseWriter) Header() http.Header { return rw.header } -// Write DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +// Deprecated: Use [net/http/httptest] instead. func (rw *TestResponseWriter) Write(bytes []byte) (int, error) { // assume 200 success if no header has been set @@ -43,7 +43,7 @@ func (rw *TestResponseWriter) Write(bytes []byte) (int, error) { } -// WriteHeader DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. +// Deprecated: Use [net/http/httptest] instead. func (rw *TestResponseWriter) WriteHeader(i int) { rw.StatusCode = i } diff --git a/http/test_round_tripper.go b/http/test_round_tripper.go index 11a024e..a0bdd71 100644 --- a/http/test_round_tripper.go +++ b/http/test_round_tripper.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/mock" ) -// TestRoundTripper DEPRECATED USE net/http/httptest +// Deprecated: Use [net/http/httptest] instead. type TestRoundTripper struct { mock.Mock } -// RoundTrip DEPRECATED USE net/http/httptest +// Deprecated: Use [net/http/httptest] instead. func (t *TestRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { args := t.Called(req) return args.Get(0).(*http.Response), args.Error(1) diff --git a/mock/mock.go b/mock/mock.go index 3234436..5e35861 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -786,7 +786,7 @@ func AnythingOfType(t string) AnythingOfTypeArgument { // for use when type checking. This is an alternative to AnythingOfType. // Used in Diff and Assert. type IsTypeArgument struct { - t interface{} + t reflect.Type } // IsType returns an IsTypeArgument object containing the type to check for. @@ -796,7 +796,7 @@ type IsTypeArgument struct { // For example: // Assert(t, IsType(""), IsType(0)) func IsType(t interface{}) *IsTypeArgument { - return &IsTypeArgument{t: t} + return &IsTypeArgument{t: reflect.TypeOf(t)} } // FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument @@ -960,53 +960,55 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { differences++ 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() { - // type checking - if reflect.TypeOf(actual).Name() != string(expected.(anythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(anythingOfTypeArgument)) { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) - } - } else if reflect.TypeOf(expected) == reflect.TypeOf((*IsTypeArgument)(nil)) { - t := expected.(*IsTypeArgument).t - if reflect.TypeOf(t) != reflect.TypeOf(actual) { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt) - } - } else if reflect.TypeOf(expected) == reflect.TypeOf((*FunctionalOptionsArgument)(nil)) { - t := expected.(*FunctionalOptionsArgument).value + } else { + switch expected := expected.(type) { + case anythingOfTypeArgument: + // type checking + if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) + } + case *IsTypeArgument: + actualT := reflect.TypeOf(actual) + if actualT != expected.t { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt) + } + case *FunctionalOptionsArgument: + t := expected.value - var name string - tValue := reflect.ValueOf(t) - if tValue.Len() > 0 { - name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() - } + var name string + tValue := reflect.ValueOf(t) + if tValue.Len() > 0 { + name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() + } - tName := reflect.TypeOf(t).Name() - if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) - } else { - if ef, af := assertOpts(t, actual); ef == "" && af == "" { + tName := reflect.TypeOf(t).Name() + if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) + } else { + if ef, af := assertOpts(t, actual); ef == "" && af == "" { + // match + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) + } else { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef) + } + } + + default: + if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { // match - output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) } else { // not match differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef) + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) } } - } else { - // normal checking - - if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { - // match - output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) - } else { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) - } } } diff --git a/mock/mock_test.go b/mock/mock_test.go index 77493c5..52d20be 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -1616,17 +1616,14 @@ func Test_Mock_IsMethodCallable(t *testing.T) { func TestIsArgsEqual(t *testing.T) { var expected = Arguments{5, 3, 4, 6, 7, 2} - var args = make([]interface{}, 5) - for i := 1; i < len(expected); i++ { - args[i-1] = expected[i] - } + + // Copy elements 1 to 5 + args := append(([]interface{})(nil), expected[1:]...) args[2] = expected[1] assert.False(t, isArgsEqual(expected, args)) - var arr = make([]interface{}, 6) - for i := 0; i < len(expected); i++ { - arr[i] = expected[i] - } + // Clone + arr := append(([]interface{})(nil), expected...) assert.True(t, isArgsEqual(expected, arr)) } diff --git a/require/require.go b/require/require.go index fa3792b..fde0833 100644 --- a/require/require.go +++ b/require/require.go @@ -1655,10 +1655,12 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, t.FailNow() } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2]) +// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1669,10 +1671,12 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i t.FailNow() } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") +// assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1877,10 +1881,11 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg t.FailNow() } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2]) +// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1891,10 +1896,11 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte t.FailNow() } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") +// assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/require/require_forward.go b/require/require_forward.go index 99e19ea..8fddd1f 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -1307,10 +1307,12 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2]) +// a.NotSubset({"x": 1, "y": 2}, {"z": 3}) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1318,10 +1320,12 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1481,10 +1485,11 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2]) +// a.Subset({"x": 1, "y": 2}, {"x": 1}) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1492,10 +1497,11 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/suite/suite.go b/suite/suite.go index 8b4202d..f3aa9a1 100644 --- a/suite/suite.go +++ b/suite/suite.go @@ -96,19 +96,20 @@ func failOnPanic(t *testing.T, r interface{}) { func (suite *Suite) Run(name string, subtest func()) bool { oldT := suite.T() - if setupSubTest, ok := suite.s.(SetupSubTest); ok { - setupSubTest.SetupSubTest() - } - - defer func() { - suite.SetT(oldT) - if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok { - tearDownSubTest.TearDownSubTest() - } - }() - return oldT.Run(name, func(t *testing.T) { suite.SetT(t) + defer suite.SetT(oldT) + + defer recoverAndFailOnPanic(t) + + if setupSubTest, ok := suite.s.(SetupSubTest); ok { + setupSubTest.SetupSubTest() + } + + if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok { + defer tearDownSubTest.TearDownSubTest() + } + subtest() }) } diff --git a/suite/suite_test.go b/suite/suite_test.go index d684f52..292dc29 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -27,14 +27,14 @@ func TestSuiteRequireTwice(t *testing.T) { ok := testing.RunTests( allTestsFilter, []testing.InternalTest{{ - Name: "TestSuiteRequireTwice", + Name: t.Name() + "/SuiteRequireTwice", F: func(t *testing.T) { suite := new(SuiteRequireTwice) Run(t, suite) }, }}, ) - assert.Equal(t, false, ok) + assert.False(t, ok) } func (s *SuiteRequireTwice) TestRequireOne() { @@ -104,31 +104,31 @@ func TestSuiteRecoverPanic(t *testing.T) { ok := true panickingTests := []testing.InternalTest{ { - Name: "TestPanicInSetupSuite", + Name: t.Name() + "/InSetupSuite", F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupSuite: true}) }, }, { - Name: "TestPanicInSetupTest", + Name: t.Name() + "/InSetupTest", F: func(t *testing.T) { Run(t, &panickingSuite{panicInSetupTest: true}) }, }, { - Name: "TestPanicInBeforeTest", + Name: t.Name() + "InBeforeTest", F: func(t *testing.T) { Run(t, &panickingSuite{panicInBeforeTest: true}) }, }, { - Name: "TestPanicInTest", + Name: t.Name() + "/InTest", F: func(t *testing.T) { Run(t, &panickingSuite{panicInTest: true}) }, }, { - Name: "TestPanicInAfterTest", + Name: t.Name() + "/InAfterTest", F: func(t *testing.T) { Run(t, &panickingSuite{panicInAfterTest: true}) }, }, { - Name: "TestPanicInTearDownTest", + Name: t.Name() + "/InTearDownTest", F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownTest: true}) }, }, { - Name: "TestPanicInTearDownSuite", + Name: t.Name() + "/InTearDownSuite", F: func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownSuite: true}) }, }, } @@ -162,6 +162,9 @@ type SuiteTester struct { SetupSubTestRunCount int TearDownSubTestRunCount int + SetupSubTestNames []string + TearDownSubTestNames []string + SuiteNameBefore []string TestNameBefore []string @@ -258,10 +261,12 @@ func (suite *SuiteTester) TestSubtest() { } func (suite *SuiteTester) TearDownSubTest() { + suite.TearDownSubTestNames = append(suite.TearDownSubTestNames, suite.T().Name()) suite.TearDownSubTestRunCount++ } func (suite *SuiteTester) SetupSubTest() { + suite.SetupSubTestNames = append(suite.SetupSubTestNames, suite.T().Name()) suite.SetupSubTestRunCount++ } @@ -301,13 +306,13 @@ func TestRunSuite(t *testing.T) { // The suite was only run once, so the SetupSuite and TearDownSuite // methods should have each been run only once. - assert.Equal(t, suiteTester.SetupSuiteRunCount, 1) - assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1) + assert.Equal(t, 1, suiteTester.SetupSuiteRunCount) + assert.Equal(t, 1, suiteTester.TearDownSuiteRunCount) - 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.Len(t, suiteTester.SuiteNameAfter, 4) + assert.Len(t, suiteTester.SuiteNameBefore, 4) + assert.Len(t, suiteTester.TestNameAfter, 4) + assert.Len(t, suiteTester.TestNameBefore, 4) assert.Contains(t, suiteTester.TestNameAfter, "TestOne") assert.Contains(t, suiteTester.TestNameAfter, "TestTwo") @@ -319,6 +324,12 @@ func TestRunSuite(t *testing.T) { assert.Contains(t, suiteTester.TestNameBefore, "TestSkip") assert.Contains(t, suiteTester.TestNameBefore, "TestSubtest") + assert.Contains(t, suiteTester.SetupSubTestNames, "TestRunSuite/TestSubtest/first") + assert.Contains(t, suiteTester.SetupSubTestNames, "TestRunSuite/TestSubtest/second") + + assert.Contains(t, suiteTester.TearDownSubTestNames, "TestRunSuite/TestSubtest/first") + assert.Contains(t, suiteTester.TearDownSubTestNames, "TestRunSuite/TestSubtest/second") + for _, suiteName := range suiteTester.SuiteNameAfter { assert.Equal(t, "SuiteTester", suiteName) } @@ -338,20 +349,20 @@ func TestRunSuite(t *testing.T) { // 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 four times. - assert.Equal(t, suiteTester.SetupTestRunCount, 4) - assert.Equal(t, suiteTester.TearDownTestRunCount, 4) + assert.Equal(t, 4, suiteTester.SetupTestRunCount) + assert.Equal(t, 4, suiteTester.TearDownTestRunCount) // 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) + assert.Equal(t, 1, suiteTester.TestOneRunCount) + assert.Equal(t, 1, suiteTester.TestTwoRunCount) + assert.Equal(t, 1, suiteTester.TestSubtestRunCount) - assert.Equal(t, suiteTester.TearDownSubTestRunCount, 2) - assert.Equal(t, suiteTester.SetupSubTestRunCount, 2) + assert.Equal(t, 2, suiteTester.TearDownSubTestRunCount) + assert.Equal(t, 2, suiteTester.SetupSubTestRunCount) // Methods that don't match the test method identifier shouldn't // have been run at all. - assert.Equal(t, suiteTester.NonTestMethodRunCount, 0) + assert.Equal(t, 0, suiteTester.NonTestMethodRunCount) suiteSkipTester := new(SuiteSkipTester) Run(t, suiteSkipTester) @@ -359,8 +370,8 @@ func TestRunSuite(t *testing.T) { // The suite was only run once, so the SetupSuite and TearDownSuite // methods should have each been run only once, even though SetupSuite // called Skip() - assert.Equal(t, suiteSkipTester.SetupSuiteRunCount, 1) - assert.Equal(t, suiteSkipTester.TearDownSuiteRunCount, 1) + assert.Equal(t, 1, suiteSkipTester.SetupSuiteRunCount) + assert.Equal(t, 1, suiteSkipTester.TearDownSuiteRunCount) } @@ -440,7 +451,7 @@ func TestSuiteLogging(t *testing.T) { suiteLoggingTester := new(SuiteLoggingTester) capture := StdoutCapture{} internalTest := testing.InternalTest{ - Name: "SomeTest", + Name: t.Name() + "/SuiteLoggingTester", F: func(subT *testing.T) { Run(subT, suiteLoggingTester) }, @@ -481,7 +492,7 @@ func (s *CallOrderSuite) 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, ";")) + assert.Equal(s.T(), "SetupSuite;SetupTest;Test A;SetupSubTest;SubTest A1;TearDownSubTest;SetupSubTest;SubTest A2;TearDownSubTest;TearDownTest;SetupTest;Test B;SetupSubTest;SubTest B1;TearDownSubTest;SetupSubTest;SubTest B2;TearDownSubTest;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) } func (s *CallOrderSuite) SetupTest() { s.call("SetupTest") @@ -491,12 +502,32 @@ func (s *CallOrderSuite) TearDownTest() { s.call("TearDownTest") } +func (s *CallOrderSuite) SetupSubTest() { + s.call("SetupSubTest") +} + +func (s *CallOrderSuite) TearDownSubTest() { + s.call("TearDownSubTest") +} + func (s *CallOrderSuite) Test_A() { s.call("Test A") + s.Run("SubTest A1", func() { + s.call("SubTest A1") + }) + s.Run("SubTest A2", func() { + s.call("SubTest A2") + }) } func (s *CallOrderSuite) Test_B() { s.call("Test B") + s.Run("SubTest B1", func() { + s.call("SubTest B1") + }) + s.Run("SubTest B2", func() { + s.call("SubTest B2") + }) } type suiteWithStats struct { @@ -521,14 +552,15 @@ func (s *suiteWithStats) TestPanic() { func TestSuiteWithStats(t *testing.T) { suiteWithStats := new(suiteWithStats) - testing.RunTests(allTestsFilter, []testing.InternalTest{ + suiteSuccess := testing.RunTests(allTestsFilter, []testing.InternalTest{ { - Name: "WithStats", + Name: t.Name() + "/suiteWithStats", F: func(t *testing.T) { Run(t, suiteWithStats) }, }, }) + require.False(t, suiteSuccess, "suiteWithStats should report test failure because of panic in TestPanic") assert.True(t, suiteWithStats.wasCalled) assert.NotZero(t, suiteWithStats.stats.Start) @@ -565,13 +597,13 @@ func TestFailfastSuite(t *testing.T) { ok := testing.RunTests( allTestsFilter, []testing.InternalTest{{ - Name: "TestFailfastSuite", + Name: t.Name() + "/FailfastSuite", F: func(t *testing.T) { Run(t, s) }, }}, ) - assert.Equal(t, false, ok) + assert.False(t, ok) if failFast { // Test A Fails and because we are running with failfast Test B never runs and we proceed straight to TearDownSuite assert.Equal(t, "SetupSuite;SetupTest;Test A Fails;TearDownTest;TearDownSuite", strings.Join(s.callOrder, ";")) @@ -617,3 +649,46 @@ func (s *FailfastSuite) Test_B_Passes() { s.call("Test B Passes") s.Require().True(true) } + +type subtestPanicSuite struct { + Suite + inTearDownSuite bool + inTearDownTest bool + inTearDownSubTest bool +} + +func (s *subtestPanicSuite) TearDownSuite() { + s.inTearDownSuite = true +} + +func (s *subtestPanicSuite) TearDownTest() { + s.inTearDownTest = true +} + +func (s *subtestPanicSuite) TearDownSubTest() { + s.inTearDownSubTest = true +} + +func (s *subtestPanicSuite) TestSubtestPanic() { + ok := s.Run("subtest", func() { + panic("panic") + }) + s.False(ok, "subtest failure is expected") +} + +func TestSubtestPanic(t *testing.T) { + suite := new(subtestPanicSuite) + ok := testing.RunTests( + allTestsFilter, + []testing.InternalTest{{ + Name: t.Name() + "/subtestPanicSuite", + F: func(t *testing.T) { + Run(t, suite) + }, + }}, + ) + assert.False(t, ok, "TestSubtestPanic/subtest should make the testsuite fail") + assert.True(t, suite.inTearDownSubTest) + assert.True(t, suite.inTearDownTest) + assert.True(t, suite.inTearDownSuite) +}