From 559d23ae666dd2dad6bc00ca82e986caff003f5b Mon Sep 17 00:00:00 2001 From: ccoVeille <3875889+ccoVeille@users.noreply.github.com> Date: Sat, 17 May 2025 16:28:06 +0200 Subject: [PATCH 01/11] check test failure message for Empty and NotEmpty Only the tests are updated, code is unchanged. Previously, the tests were checking only the result of the asserter. Using captureTestingT helper allows to check the error message --- assert/assertions_test.go | 198 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index f3d41a5ac..05c81b9e1 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1704,6 +1704,142 @@ func TestEmpty(t *testing.T) { False(t, Empty(mockT, TString("abc")), "non-empty aliased string is empty") False(t, Empty(mockT, xP), "ptr to non-nil value is not empty") False(t, Empty(mockT, [1]int{42}), "array is not state") + + // error messages validation + tests := []struct { + name string + value interface{} + expectedResult bool + expectedErrMsg string + }{ + { + name: "Non Empty string is not empty", + value: "something", + expectedResult: false, + expectedErrMsg: "Should be empty, but was something\n", + }, + { + name: "Non nil object is not empty", + value: errors.New("something"), + expectedResult: false, + expectedErrMsg: "Should be empty, but was something\n", + }, + { + name: "Non empty string array is not empty", + value: []string{"something"}, + expectedResult: false, + expectedErrMsg: "Should be empty, but was [something]\n", + }, + { + name: "Non-zero int value is not empty", + value: 1, + expectedResult: false, + expectedErrMsg: "Should be empty, but was 1\n", + }, + { + name: "True value is not empty", + value: true, + expectedResult: false, + expectedErrMsg: "Should be empty, but was true\n", + }, + { + name: "Channel with values is not empty", + value: chWithValue, + expectedResult: false, + expectedErrMsg: fmt.Sprintf("Should be empty, but was %v\n", chWithValue), + }, + { + name: "struct with initialized values is empty", + value: TStruct{x: 1}, + expectedResult: false, + expectedErrMsg: "Should be empty, but was {1}\n", + }, + { + name: "non-empty aliased string is empty", + value: TString("abc"), + expectedResult: false, + expectedErrMsg: "Should be empty, but was abc\n", + }, + { + name: "ptr to non-nil value is not empty", + value: xP, + expectedResult: false, + expectedErrMsg: fmt.Sprintf("Should be empty, but was %p\n", xP), + }, + { + name: "array is not state", + value: [1]int{42}, + expectedResult: false, + expectedErrMsg: "Should be empty, but was [42]\n", + }, + + // Here are some edge cases + { + name: "string with only spaces is not empty", + value: " ", + expectedResult: false, + expectedErrMsg: "Should be empty, but was \n", // TODO FIX THIS strange error message + }, + { + name: "string with a line feed is not empty", + value: "\n", + expectedResult: false, + // TODO This is the exact same error message as for an empty string + expectedErrMsg: "Should be empty, but was \n", // TODO FIX THIS strange error message + }, + { + name: "string with only tabulation and lines feed is not empty", + value: "\n\t\n", + expectedResult: false, + // TODO The line feeds and tab are not helping to spot what is expected + expectedErrMsg: "" + // this syntax is used to show how errors are reported. + "Should be empty, but was \n" + + "\t\n", + }, + { + name: "string with trailing lines feed is not empty", + value: "foo\n\n", + expectedResult: false, + // TODO it's not clear if one or two lines feed are expected + expectedErrMsg: "Should be empty, but was foo\n\n", + }, + { + name: "string with leading and trailing tabulation and lines feed is not empty", + value: "\n\nfoo\t\n\t\n", + expectedResult: false, + // TODO The line feeds and tab are not helping to figure what is expected + expectedErrMsg: "" + + "Should be empty, but was \n" + + "\n" + + "foo\t\n" + + "\t\n", + }, + + { + name: "non-printable character is not empty", + value: "\u00a0", // NO-BREAK SPACE UNICODE CHARACTER + expectedResult: false, + // TODO here you cannot figure out what is expected + expectedErrMsg: "Should be empty, but was \u00a0\n", + }, + + // Here we are testing there is no error message on success + { + name: "Empty string is empty", + value: "", + expectedResult: true, + expectedErrMsg: "", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + mockCT := new(captureTestingT) + res := Empty(mockCT, tt.value) + mockCT.checkResultAndErrMsg(t, res, tt.expectedResult, tt.expectedErrMsg) + }) + } } func TestNotEmpty(t *testing.T) { @@ -1726,6 +1862,68 @@ func TestNotEmpty(t *testing.T) { True(t, NotEmpty(mockT, true), "True value is not empty") True(t, NotEmpty(mockT, chWithValue), "Channel with values is not empty") True(t, NotEmpty(mockT, [1]int{42}), "array is not state") + + // error messages validation + tests := []struct { + name string + value interface{} + expectedResult bool + expectedErrMsg string + }{ + { + name: "Empty string is empty", + value: "", + expectedResult: false, + expectedErrMsg: `Should NOT be empty, but was ` + "\n", // TODO FIX THIS strange error message + }, + { + name: "Nil is empty", + value: nil, + expectedResult: false, + expectedErrMsg: "Should NOT be empty, but was \n", + }, + { + name: "Empty string array is empty", + value: []string{}, + expectedResult: false, + expectedErrMsg: "Should NOT be empty, but was []\n", + }, + { + name: "Zero int value is empty", + value: 0, + expectedResult: false, + expectedErrMsg: "Should NOT be empty, but was 0\n", + }, + { + name: "False value is empty", + value: false, + expectedResult: false, + expectedErrMsg: "Should NOT be empty, but was false\n", + }, + { + name: "array is state", + value: [1]int{}, + expectedResult: false, + expectedErrMsg: "Should NOT be empty, but was [0]\n", + }, + + // Here we are testing there is no error message on success + { + name: "Non Empty string is not empty", + value: "something", + expectedResult: true, + expectedErrMsg: "", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + mockCT := new(captureTestingT) + res := NotEmpty(mockCT, tt.value) + mockCT.checkResultAndErrMsg(t, tt.expectedResult, res, tt.expectedErrMsg) + }) + } } func Test_getLen(t *testing.T) { From e2ad95950ec6d51233ace09f87ae47ee96de93c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 11 Jul 2023 21:55:54 +0200 Subject: [PATCH 02/11] mock/tests: add method Helper() to all mock of *testing.T --- mock/mock_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mock/mock_test.go b/mock/mock_test.go index 04664d44b..2dea0873b 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -132,6 +132,9 @@ type MockTestingT struct { logfCount, errorfCount, failNowCount int } +// Helper is like [testing.T.Helper] but does nothing. +func (MockTestingT) Helper() {} + const mockTestingTFailNowCalled = "FailNow was called" func (m *MockTestingT) Logf(string, ...interface{}) { From 8f73f15d6939f4720ea8442714fdce556873b785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 11 Jul 2023 22:17:10 +0200 Subject: [PATCH 03/11] assert.CollectT: add Helper() method Add Helper() method to CollectT like testing.T as we intend to add Helper() to the assert.TestingT interface. --- assert/assertions.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assert/assertions.go b/assert/assertions.go index bdead180f..adac3ed4e 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -2008,6 +2008,9 @@ type CollectT struct { errors []error } +// Helper is like [testing.T.Helper] but does nothing. +func (CollectT) Helper() {} + // Errorf collects the error. func (c *CollectT) Errorf(format string, args ...interface{}) { c.errors = append(c.errors, fmt.Errorf(format, args...)) From 3c1541a3b402701ac9e9e6d5d75e5933e5f0014d Mon Sep 17 00:00:00 2001 From: ccoVeille <3875889+ccoVeille@users.noreply.github.com> Date: Tue, 13 May 2025 15:10:26 +0200 Subject: [PATCH 04/11] Improve usage of Sprintf with Same/NotSame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Olivier Mengué --- assert/assertions.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index adac3ed4e..effd026d5 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -543,8 +543,9 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b if !same { // both are pointers but not the same type & pointing to the same address return Fail(t, fmt.Sprintf("Not same: \n"+ - "expected: %p %#v\n"+ - "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) + "expected: %p %#[1]v\n"+ + "actual : %p %#[2]v", + expected, actual), msgAndArgs...) } return true @@ -569,8 +570,8 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} if same { return Fail(t, fmt.Sprintf( - "Expected and actual point to the same object: %p %#v", - expected, expected), msgAndArgs...) + "Expected and actual point to the same object: %p %#[1]v", + expected), msgAndArgs...) } return true } From 4eb688ba0c181ce495062311336ff9f419ab7385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Wed, 28 May 2025 18:51:47 +0200 Subject: [PATCH 05/11] assert.JSONEq: shortcut if same strings Shortcut in assert.JSONEq once we have validated that 'expected' is valid JSON, and 'actual' is the exact same string. --- assert/assertions.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/assert/assertions.go b/assert/assertions.go index effd026d5..6fc02a340 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1853,6 +1853,11 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) } + // Shortcut if same bytes + if actual == expected { + return true + } + if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) } From 4f71159ca8c6fffb34384e7e2eff2b3c4efdf988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Wed, 28 May 2025 19:11:19 +0200 Subject: [PATCH 06/11] assert.YAMLEq: shortcut if same strings Shortcut in assert.YAMLEq once we have validated that 'expected' is valid YAML, and 'actual' is the exact same string. --- assert/assertions.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/assert/assertions.go b/assert/assertions.go index 6fc02a340..9913ac2f0 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1876,6 +1876,11 @@ func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...) } + // Shortcut if same bytes + if actual == expected { + return true + } + if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...) } From 8b3dc18b37cc18e399999823e3b4bf327ac41f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Wed, 28 May 2025 21:49:17 +0200 Subject: [PATCH 07/11] mock: enable parallel testing on internal testsuite Add t.Parallel() to all package-level tests of the 'mock' package. Result: faster tests results. Before: $ go test -count=10 ./mock ok github.com/stretchr/testify/mock 0.631s After: $ go test -count=10 ./mock ok github.com/stretchr/testify/mock 0.426s --- mock/mock_test.go | 166 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/mock/mock_test.go b/mock/mock_test.go index 2dea0873b..813ec5e56 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -163,6 +163,7 @@ func (m *MockTestingT) FailNow() { */ func Test_Mock_TestData(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -174,6 +175,7 @@ func Test_Mock_TestData(t *testing.T) { } func Test_Mock_On(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -184,6 +186,8 @@ func Test_Mock_On(t *testing.T) { } func Test_Mock_Chained_On(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -215,6 +219,7 @@ func Test_Mock_Chained_On(t *testing.T) { } func Test_Mock_On_WithArgs(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -227,6 +232,7 @@ func Test_Mock_On_WithArgs(t *testing.T) { } func Test_Mock_On_WithFuncArg(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -248,6 +254,8 @@ func Test_Mock_On_WithFuncArg(t *testing.T) { } func Test_Mock_On_WithIntArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod", @@ -271,6 +279,8 @@ func Test_Mock_On_WithIntArgMatcher(t *testing.T) { } func Test_Mock_On_WithArgMatcherThatPanics(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod2", MatchedBy(func(_ interface{}) bool { @@ -297,6 +307,8 @@ func Test_Mock_On_WithArgMatcherThatPanics(t *testing.T) { } func TestMock_WithTest(t *testing.T) { + t.Parallel() + var ( mockedService TestExampleImplementation mockedTest MockTestingT @@ -325,6 +337,8 @@ func TestMock_WithTest(t *testing.T) { } func Test_Mock_On_WithPtrArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod3", @@ -345,6 +359,8 @@ func Test_Mock_On_WithPtrArgMatcher(t *testing.T) { } func Test_Mock_On_WithFuncArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation fixture1, fixture2 := errors.New("fixture1"), errors.New("fixture2") @@ -369,6 +385,8 @@ func Test_Mock_On_WithFuncArgMatcher(t *testing.T) { } func Test_Mock_On_WithInterfaceArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod4", @@ -379,6 +397,8 @@ func Test_Mock_On_WithInterfaceArgMatcher(t *testing.T) { } func Test_Mock_On_WithChannelArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod5", @@ -389,6 +409,8 @@ func Test_Mock_On_WithChannelArgMatcher(t *testing.T) { } func Test_Mock_On_WithMapArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod6", @@ -399,6 +421,8 @@ func Test_Mock_On_WithMapArgMatcher(t *testing.T) { } func Test_Mock_On_WithSliceArgMatcher(t *testing.T) { + t.Parallel() + var mockedService TestExampleImplementation mockedService.On("TheExampleMethod7", @@ -409,6 +433,7 @@ func Test_Mock_On_WithSliceArgMatcher(t *testing.T) { } func Test_Mock_On_WithVariadicFunc(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -431,6 +456,7 @@ func Test_Mock_On_WithVariadicFunc(t *testing.T) { } func Test_Mock_On_WithMixedVariadicFunc(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -454,6 +480,7 @@ func Test_Mock_On_WithMixedVariadicFunc(t *testing.T) { } func Test_Mock_On_WithVariadicFuncWithInterface(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -475,6 +502,7 @@ func Test_Mock_On_WithVariadicFuncWithInterface(t *testing.T) { } func Test_Mock_On_WithVariadicFuncWithEmptyInterfaceArray(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -498,6 +526,8 @@ func Test_Mock_On_WithVariadicFuncWithEmptyInterfaceArray(t *testing.T) { } func Test_Mock_On_WithFuncPanics(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -507,6 +537,7 @@ func Test_Mock_On_WithFuncPanics(t *testing.T) { } func Test_Mock_On_WithFuncTypeArg(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -526,6 +557,8 @@ func Test_Mock_On_WithFuncTypeArg(t *testing.T) { } func Test_Mock_Unset(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -554,6 +587,8 @@ func Test_Mock_Unset(t *testing.T) { // Since every time you call On it creates a new object // the last time you call Unset it will only unset the last call func Test_Mock_Chained_UnsetOnlyUnsetsLastCall(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -588,6 +623,8 @@ func Test_Mock_Chained_UnsetOnlyUnsetsLastCall(t *testing.T) { } func Test_Mock_UnsetIfAlreadyUnsetFails(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -607,6 +644,8 @@ func Test_Mock_UnsetIfAlreadyUnsetFails(t *testing.T) { } func Test_Mock_UnsetByOnMethodSpec(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -628,6 +667,8 @@ func Test_Mock_UnsetByOnMethodSpec(t *testing.T) { } func Test_Mock_UnsetByOnMethodSpecAmongOthers(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) @@ -670,6 +711,8 @@ func Test_Mock_UnsetByOnMethodSpecAmongOthers(t *testing.T) { } func Test_Mock_Unset_WithFuncPanics(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) mock1 := mockedService.On("TheExampleMethod", 1) @@ -681,6 +724,7 @@ func Test_Mock_Unset_WithFuncPanics(t *testing.T) { } func Test_Mock_Return(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -705,6 +749,7 @@ func Test_Mock_Return(t *testing.T) { } func Test_Mock_Panic(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -728,6 +773,7 @@ func Test_Mock_Panic(t *testing.T) { } func Test_Mock_Return_WaitUntil(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -755,6 +801,7 @@ func Test_Mock_Return_WaitUntil(t *testing.T) { } func Test_Mock_Return_After(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -781,6 +828,7 @@ func Test_Mock_Return_After(t *testing.T) { } func Test_Mock_Return_Run(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -813,6 +861,8 @@ func Test_Mock_Return_Run(t *testing.T) { } func Test_Mock_Return_Run_Out_Of_Order(t *testing.T) { + t.Parallel() + // make a test impl object var mockedService = new(TestExampleImplementation) f := func(args Arguments) { @@ -838,6 +888,7 @@ func Test_Mock_Return_Run_Out_Of_Order(t *testing.T) { } func Test_Mock_Return_Once(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -862,6 +913,7 @@ func Test_Mock_Return_Once(t *testing.T) { } func Test_Mock_Return_Twice(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -887,6 +939,7 @@ func Test_Mock_Return_Twice(t *testing.T) { } func Test_Mock_Return_Times(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -912,6 +965,7 @@ func Test_Mock_Return_Times(t *testing.T) { } func Test_Mock_Return_Nothing(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -932,6 +986,8 @@ func Test_Mock_Return_Nothing(t *testing.T) { } func Test_Mock_Return_NotBefore_In_Order(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) b := mockedService. @@ -952,6 +1008,8 @@ func Test_Mock_Return_NotBefore_In_Order(t *testing.T) { } func Test_Mock_Return_InOrder_Uses_NotBefore(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) InOrder( @@ -972,6 +1030,8 @@ func Test_Mock_Return_InOrder_Uses_NotBefore(t *testing.T) { } func Test_Mock_Return_NotBefore_Out_Of_Order(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) b := mockedService. @@ -1002,6 +1062,8 @@ TheExampleMethod(int,int,int) } func Test_Mock_Return_InOrder_Uses_NotBefore_Out_Of_Order(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) InOrder( @@ -1031,6 +1093,8 @@ TheExampleMethod(int,int,int) } func Test_Mock_Return_NotBefore_Not_Enough_Times(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) b := mockedService. @@ -1064,6 +1128,8 @@ TheExampleMethod(int,int,int) } func Test_Mock_Return_NotBefore_Different_Mock_In_Order(t *testing.T) { + t.Parallel() + var ( mockedService1 = new(TestExampleImplementation) mockedService2 = new(TestExampleImplementation) @@ -1087,6 +1153,8 @@ func Test_Mock_Return_NotBefore_Different_Mock_In_Order(t *testing.T) { } func Test_Mock_Return_NotBefore_Different_Mock_Out_Of_Order(t *testing.T) { + t.Parallel() + var ( mockedService1 = new(TestExampleImplementation) mockedService2 = new(TestExampleImplementation) @@ -1120,6 +1188,8 @@ TheExampleMethod(int,int,int) } func Test_Mock_Return_NotBefore_In_Order_With_Non_Dependant(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) a := mockedService. @@ -1160,6 +1230,8 @@ func Test_Mock_Return_NotBefore_In_Order_With_Non_Dependant(t *testing.T) { } func Test_Mock_Return_NotBefore_Orphan_Call(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) require.PanicsWithValue(t, "not before calls must be created with Mock.On()", func() { @@ -1171,6 +1243,7 @@ func Test_Mock_Return_NotBefore_Orphan_Call(t *testing.T) { } func Test_Mock_findExpectedCall(t *testing.T) { + t.Parallel() m := new(Mock) m.On("One", 1).Return("one") @@ -1190,6 +1263,7 @@ func Test_Mock_findExpectedCall(t *testing.T) { } func Test_Mock_findExpectedCall_For_Unknown_Method(t *testing.T) { + t.Parallel() m := new(Mock) m.On("One", 1).Return("one") @@ -1203,6 +1277,7 @@ func Test_Mock_findExpectedCall_For_Unknown_Method(t *testing.T) { } func Test_Mock_findExpectedCall_Respects_Repeatability(t *testing.T) { + t.Parallel() m := new(Mock) m.On("One", 1).Return("one") @@ -1233,6 +1308,7 @@ func Test_Mock_findExpectedCall_Respects_Repeatability(t *testing.T) { } func Test_callString(t *testing.T) { + t.Parallel() assert.Equal(t, `Method(int,bool,string)`, callString("Method", []interface{}{1, true, "something"}, false)) assert.Equal(t, `Method()`, callString("Method", []interface{}{nil}, false)) @@ -1240,6 +1316,7 @@ func Test_callString(t *testing.T) { } func Test_Mock_Called(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1267,6 +1344,7 @@ func asyncCall(m *Mock, ch chan Arguments) { } func Test_Mock_Called_blocks(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1300,6 +1378,7 @@ func Test_Mock_Called_blocks(t *testing.T) { } func Test_Mock_Called_For_Bounded_Repeatability(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1341,6 +1420,7 @@ func Test_Mock_Called_For_Bounded_Repeatability(t *testing.T) { } func Test_Mock_Called_For_SetTime_Expectation(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1357,6 +1437,7 @@ func Test_Mock_Called_For_SetTime_Expectation(t *testing.T) { } func Test_Mock_Called_Unexpected(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1368,6 +1449,7 @@ func Test_Mock_Called_Unexpected(t *testing.T) { } func Test_AssertExpectationsForObjects_Helper(t *testing.T) { + t.Parallel() var mockedService1 = new(TestExampleImplementation) var mockedService2 = new(TestExampleImplementation) @@ -1388,6 +1470,7 @@ func Test_AssertExpectationsForObjects_Helper(t *testing.T) { } func Test_AssertExpectationsForObjects_Helper_Failed(t *testing.T) { + t.Parallel() var mockedService1 = new(TestExampleImplementation) var mockedService2 = new(TestExampleImplementation) @@ -1407,6 +1490,7 @@ func Test_AssertExpectationsForObjects_Helper_Failed(t *testing.T) { } func Test_Mock_AssertExpectations(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1424,6 +1508,7 @@ func Test_Mock_AssertExpectations(t *testing.T) { } func Test_Mock_AssertExpectations_Placeholder_NoArgs(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1442,6 +1527,7 @@ func Test_Mock_AssertExpectations_Placeholder_NoArgs(t *testing.T) { } func Test_Mock_AssertExpectations_Placeholder(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1465,6 +1551,7 @@ func Test_Mock_AssertExpectations_Placeholder(t *testing.T) { } func Test_Mock_AssertExpectations_With_Pointers(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1486,6 +1573,7 @@ func Test_Mock_AssertExpectations_With_Pointers(t *testing.T) { } func Test_Mock_AssertExpectationsCustomType(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1503,6 +1591,7 @@ func Test_Mock_AssertExpectationsCustomType(t *testing.T) { } func Test_Mock_AssertExpectationsFunctionalOptionsType(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1520,6 +1609,7 @@ func Test_Mock_AssertExpectationsFunctionalOptionsType(t *testing.T) { } func Test_Mock_AssertExpectationsFunctionalOptionsType_Empty(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1537,6 +1627,7 @@ func Test_Mock_AssertExpectationsFunctionalOptionsType_Empty(t *testing.T) { } func Test_Mock_AssertExpectationsFunctionalOptionsType_Indirectly(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1554,6 +1645,7 @@ func Test_Mock_AssertExpectationsFunctionalOptionsType_Indirectly(t *testing.T) } func Test_Mock_AssertExpectationsFunctionalOptionsType_Diff_Func(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1568,6 +1660,7 @@ func Test_Mock_AssertExpectationsFunctionalOptionsType_Diff_Func(t *testing.T) { } func Test_Mock_AssertExpectationsFunctionalOptionsType_Diff_Arg(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1582,6 +1675,7 @@ func Test_Mock_AssertExpectationsFunctionalOptionsType_Diff_Arg(t *testing.T) { } func Test_Mock_AssertExpectations_With_Repeatability(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1603,6 +1697,7 @@ func Test_Mock_AssertExpectations_With_Repeatability(t *testing.T) { } func Test_Mock_AssertExpectations_Skipped_Test(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1613,6 +1708,7 @@ func Test_Mock_AssertExpectations_Skipped_Test(t *testing.T) { } func Test_Mock_TwoCallsWithDifferentArguments(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1632,6 +1728,7 @@ func Test_Mock_TwoCallsWithDifferentArguments(t *testing.T) { } func Test_Mock_AssertNumberOfCalls(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1646,6 +1743,7 @@ func Test_Mock_AssertNumberOfCalls(t *testing.T) { } func Test_Mock_AssertCalled(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1658,6 +1756,7 @@ func Test_Mock_AssertCalled(t *testing.T) { } func Test_Mock_AssertCalled_WithAnythingOfTypeArgument(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1672,6 +1771,7 @@ func Test_Mock_AssertCalled_WithAnythingOfTypeArgument(t *testing.T) { } func Test_Mock_AssertCalled_WithArguments(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1686,6 +1786,7 @@ func Test_Mock_AssertCalled_WithArguments(t *testing.T) { } func Test_Mock_AssertCalled_WithArguments_With_Repeatability(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1703,6 +1804,7 @@ func Test_Mock_AssertCalled_WithArguments_With_Repeatability(t *testing.T) { } func Test_Mock_AssertNotCalled(t *testing.T) { + t.Parallel() var mockedService = new(TestExampleImplementation) @@ -1715,6 +1817,8 @@ func Test_Mock_AssertNotCalled(t *testing.T) { } func Test_Mock_IsMethodCallable(t *testing.T) { + t.Parallel() + var mockedService = new(TestExampleImplementation) arg := []Call{{Repeatability: 1}, {Repeatability: 2}} @@ -1734,6 +1838,8 @@ func Test_Mock_IsMethodCallable(t *testing.T) { } func TestIsArgsEqual(t *testing.T) { + t.Parallel() + var expected = Arguments{5, 3, 4, 6, 7, 2} // Copy elements 1 to 5 @@ -1747,6 +1853,8 @@ func TestIsArgsEqual(t *testing.T) { } func Test_Mock_AssertOptional(t *testing.T) { + t.Parallel() + // Optional called var ms1 = new(TestExampleImplementation) ms1.On("TheExampleMethod", 1, 2, 3).Maybe().Return(4, nil) @@ -1775,6 +1883,7 @@ func Test_Mock_AssertOptional(t *testing.T) { Arguments helper methods */ func Test_Arguments_Get(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) @@ -1785,6 +1894,7 @@ func Test_Arguments_Get(t *testing.T) { } func Test_Arguments_Is(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) @@ -1794,6 +1904,7 @@ func Test_Arguments_Is(t *testing.T) { } func Test_Arguments_Diff(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"Hello World", 123, true}) var diff string @@ -1807,6 +1918,7 @@ func Test_Arguments_Diff(t *testing.T) { } func Test_Arguments_Diff_DifferentNumberOfArgs(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) var diff string @@ -1819,6 +1931,7 @@ func Test_Arguments_Diff_DifferentNumberOfArgs(t *testing.T) { } func Test_Arguments_Diff_WithAnythingArgument(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) var count int @@ -1829,6 +1942,7 @@ func Test_Arguments_Diff_WithAnythingArgument(t *testing.T) { } func Test_Arguments_Diff_WithAnythingArgument_InActualToo(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", Anything, true}) var count int @@ -1839,6 +1953,7 @@ func Test_Arguments_Diff_WithAnythingArgument_InActualToo(t *testing.T) { } func Test_Arguments_Diff_WithAnythingOfTypeArgument(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", AnythingOfType("int"), true}) var count int @@ -1849,6 +1964,7 @@ func Test_Arguments_Diff_WithAnythingOfTypeArgument(t *testing.T) { } func Test_Arguments_Diff_WithAnythingOfTypeArgument_Failing(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", AnythingOfType("string"), true}) var count int @@ -1861,6 +1977,8 @@ func Test_Arguments_Diff_WithAnythingOfTypeArgument_Failing(t *testing.T) { } func Test_Arguments_Diff_WithIsTypeArgument(t *testing.T) { + t.Parallel() + var args = Arguments([]interface{}{"string", IsType(0), true}) var count int _, count = args.Diff([]interface{}{"string", 123, true}) @@ -1869,6 +1987,8 @@ func Test_Arguments_Diff_WithIsTypeArgument(t *testing.T) { } func Test_Arguments_Diff_WithIsTypeArgument_Failing(t *testing.T) { + t.Parallel() + var args = Arguments([]interface{}{"string", IsType(""), true}) var count int var diff string @@ -1879,6 +1999,8 @@ func Test_Arguments_Diff_WithIsTypeArgument_Failing(t *testing.T) { } func Test_Arguments_Diff_WithArgMatcher(t *testing.T) { + t.Parallel() + matchFn := func(a int) bool { return a == 123 } @@ -1902,6 +2024,7 @@ func Test_Arguments_Diff_WithArgMatcher(t *testing.T) { } func Test_Arguments_Assert(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) @@ -1910,6 +2033,7 @@ func Test_Arguments_Assert(t *testing.T) { } func Test_Arguments_String_Representation(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) assert.Equal(t, `string,int,bool`, args.String()) @@ -1917,6 +2041,7 @@ func Test_Arguments_String_Representation(t *testing.T) { } func Test_Arguments_String(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) assert.Equal(t, "string", args.String(0)) @@ -1924,6 +2049,7 @@ func Test_Arguments_String(t *testing.T) { } func Test_Arguments_Error(t *testing.T) { + t.Parallel() var err = errors.New("An Error") var args = Arguments([]interface{}{"string", 123, true, err}) @@ -1932,6 +2058,7 @@ func Test_Arguments_Error(t *testing.T) { } func Test_Arguments_Error_Nil(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true, nil}) assert.Equal(t, nil, args.Error(3)) @@ -1939,6 +2066,7 @@ func Test_Arguments_Error_Nil(t *testing.T) { } func Test_Arguments_Int(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) assert.Equal(t, 123, args.Int(1)) @@ -1946,6 +2074,7 @@ func Test_Arguments_Int(t *testing.T) { } func Test_Arguments_Bool(t *testing.T) { + t.Parallel() var args = Arguments([]interface{}{"string", 123, true}) assert.Equal(t, true, args.Bool(2)) @@ -1953,6 +2082,7 @@ func Test_Arguments_Bool(t *testing.T) { } func Test_WaitUntil_Parallel(t *testing.T) { + t.Parallel() // make a test impl object var mockedService = new(TestExampleImplementation) @@ -1979,6 +2109,8 @@ func Test_WaitUntil_Parallel(t *testing.T) { } func Test_MockMethodCalled(t *testing.T) { + t.Parallel() + m := new(Mock) m.On("foo", "hello").Return("world") @@ -1989,6 +2121,8 @@ func Test_MockMethodCalled(t *testing.T) { } func Test_MockMethodCalled_Panic(t *testing.T) { + t.Parallel() + m := new(Mock) m.On("foo", "hello").Panic("world panics") @@ -1998,6 +2132,8 @@ func Test_MockMethodCalled_Panic(t *testing.T) { // Test to validate fix for racy concurrent call access in MethodCalled() func Test_MockReturnAndCalledConcurrent(t *testing.T) { + t.Parallel() + iterations := 1000 m := &Mock{} call := m.On("ConcurrencyTestMethod") @@ -2048,6 +2184,8 @@ func (tc *tCustomLogger) Errorf(format string, args ...interface{}) { func (tc *tCustomLogger) FailNow() {} func TestLoggingAssertExpectations(t *testing.T) { + t.Parallel() + m := new(timer) m.On("GetTime", 0).Return("") tcl := &tCustomLogger{t, []string{}, []string{}} @@ -2062,6 +2200,8 @@ func TestLoggingAssertExpectations(t *testing.T) { } func TestAfterTotalWaitTimeWhileExecution(t *testing.T) { + t.Parallel() + waitDuration := 1 total, waitMs := 5, time.Millisecond*time.Duration(waitDuration) aTimer := new(timer) @@ -2086,6 +2226,8 @@ func TestAfterTotalWaitTimeWhileExecution(t *testing.T) { } func TestArgumentMatcherToPrintMismatch(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { matchingExp := regexp.MustCompile( @@ -2103,6 +2245,8 @@ func TestArgumentMatcherToPrintMismatch(t *testing.T) { } func TestArgumentMatcherToPrintMismatchWithReferenceType(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { matchingExp := regexp.MustCompile( @@ -2120,6 +2264,8 @@ func TestArgumentMatcherToPrintMismatchWithReferenceType(t *testing.T) { } func TestClosestCallMismatchedArgumentInformationShowsTheClosest(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { matchingExp := regexp.MustCompile(unexpectedCallRegex(`TheExampleMethod(int,int,int)`, `0: 1\s+1: 1\s+2: 2`, `0: 1\s+1: 1\s+2: 1`, `Diff: 0: PASS: \(int=1\) == \(int=1\)\s+1: PASS: \(int=1\) == \(int=1\)\s+2: FAIL: \(int=2\) != \(int=1\)`)) @@ -2135,6 +2281,8 @@ func TestClosestCallMismatchedArgumentInformationShowsTheClosest(t *testing.T) { } func TestClosestCallFavorsFirstMock(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -2,4 \+2,4 @@\s+\(bool\) true,\s+- \(bool\) true,\s+- \(bool\) true\s+\+ \(bool\) false,\s+\+ \(bool\) false\s+}\s+Diff: 0: FAIL: \(\[\]bool=\[(true\s?|false\s?){3}]\) != \(\[\]bool=\[(true\s?|false\s?){3}\]\)` @@ -2151,6 +2299,8 @@ func TestClosestCallFavorsFirstMock(t *testing.T) { } func TestClosestCallUsesRepeatabilityToFindClosest(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { diffRegExp := `Difference found in argument 0:\s+--- Expected\s+\+\+\+ Actual\s+@@ -1,4 \+1,4 @@\s+\(\[\]bool\) \(len=3\) {\s+- \(bool\) false,\s+- \(bool\) false,\s+\+ \(bool\) true,\s+\+ \(bool\) true,\s+\(bool\) false\s+Diff: 0: FAIL: \(\[\]bool=\[(true\s?|false\s?){3}]\) != \(\[\]bool=\[(true\s?|false\s?){3}\]\)` @@ -2171,6 +2321,8 @@ func TestClosestCallUsesRepeatabilityToFindClosest(t *testing.T) { } func TestClosestCallMismatchedArgumentValueInformation(t *testing.T) { + t.Parallel() + defer func() { if r := recover(); r != nil { matchingExp := regexp.MustCompile(unexpectedCallRegex(`GetTime(int)`, "0: 1", "0: 999", `Diff: 0: FAIL: \(int=1\) != \(int=999\)`)) @@ -2185,26 +2337,38 @@ func TestClosestCallMismatchedArgumentValueInformation(t *testing.T) { } func Test_isBetterMatchThanReturnsFalseIfCandidateCallIsNil(t *testing.T) { + t.Parallel() + assert.False(t, matchCandidate{}.isBetterMatchThan(matchCandidate{})) } func Test_isBetterMatchThanReturnsTrueIfOtherCandidateCallIsNil(t *testing.T) { + t.Parallel() + assert.True(t, matchCandidate{call: &Call{}}.isBetterMatchThan(matchCandidate{})) } func Test_isBetterMatchThanReturnsFalseIfDiffCountIsGreaterThanOther(t *testing.T) { + t.Parallel() + assert.False(t, matchCandidate{call: &Call{}, diffCount: 2}.isBetterMatchThan(matchCandidate{call: &Call{}, diffCount: 1})) } func Test_isBetterMatchThanReturnsTrueIfDiffCountIsLessThanOther(t *testing.T) { + t.Parallel() + assert.True(t, matchCandidate{call: &Call{}, diffCount: 1}.isBetterMatchThan(matchCandidate{call: &Call{}, diffCount: 2})) } func Test_isBetterMatchThanReturnsTrueIfRepeatabilityIsGreaterThanOther(t *testing.T) { + t.Parallel() + assert.True(t, matchCandidate{call: &Call{Repeatability: 1}, diffCount: 1}.isBetterMatchThan(matchCandidate{call: &Call{Repeatability: -1}, diffCount: 1})) } func Test_isBetterMatchThanReturnsFalseIfRepeatabilityIsLessThanOrEqualToOther(t *testing.T) { + t.Parallel() + assert.False(t, matchCandidate{call: &Call{Repeatability: 1}, diffCount: 1}.isBetterMatchThan(matchCandidate{call: &Call{Repeatability: 1}, diffCount: 1})) } @@ -2220,6 +2384,8 @@ func ConcurrencyTestMethod(m *Mock) { } func TestConcurrentArgumentRead(t *testing.T) { + t.Parallel() + methodUnderTest := func(c caller, u user) { go u.Use(c) c.Call() From 82767aed18541a8271db7757a5140894b469e0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Tue, 27 May 2025 17:54:26 +0200 Subject: [PATCH 08/11] suite: cleanup use of 'testing' internals at runtime Cleanup runtime use of stdlib's testing internals which was required for older Go versions. Note: we are still using testing.RunTests in the suite's test suite for now. --- suite/suite.go | 30 +++++++++++------------------- suite/suite_test.go | 5 +++++ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/suite/suite.go b/suite/suite.go index 18443a91c..7c79250bb 100644 --- a/suite/suite.go +++ b/suite/suite.go @@ -15,7 +15,6 @@ import ( "github.com/stretchr/testify/require" ) -var allTestsFilter = func(_, _ string) (bool, error) { return true, nil } var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run") // Suite is a basic testing suite with methods for storing and @@ -116,6 +115,11 @@ func (suite *Suite) Run(name string, subtest func()) bool { }) } +type test = struct { + name string + run func(t *testing.T) +} + // Run takes a testing suite and runs all of the tests attached // to it. func Run(t *testing.T, suite TestingSuite) { @@ -131,7 +135,7 @@ func Run(t *testing.T, suite TestingSuite) { stats = newSuiteInformation() } - tests := []testing.InternalTest{} + var tests []test methodFinder := reflect.TypeOf(suite) suiteName := methodFinder.Elem().Name() @@ -160,9 +164,9 @@ func Run(t *testing.T, suite TestingSuite) { suiteSetupDone = true } - test := testing.InternalTest{ - Name: method.Name, - F: func(t *testing.T) { + test := test{ + name: method.Name, + run: func(t *testing.T) { parentT := suite.T() suite.SetT(t) defer recoverAndFailOnPanic(t) @@ -229,25 +233,13 @@ func methodFilter(name string) (bool, error) { return regexp.MatchString(*matchMethod, name) } -func runTests(t testing.TB, tests []testing.InternalTest) { +func runTests(t *testing.T, tests []test) { if len(tests) == 0 { t.Log("warning: no tests to run") return } - r, ok := t.(runner) - if !ok { // backwards compatibility with Go 1.6 and below - if !testing.RunTests(allTestsFilter, tests) { - t.Fail() - } - return - } - for _, test := range tests { - r.Run(test.Name, test.F) + t.Run(test.name, test.run) } } - -type runner interface { - Run(name string, f func(t *testing.T)) bool -} diff --git a/suite/suite_test.go b/suite/suite_test.go index eceecb622..6cf23459f 100644 --- a/suite/suite_test.go +++ b/suite/suite_test.go @@ -16,6 +16,11 @@ import ( "github.com/stretchr/testify/require" ) +// allTestsFilter is a yes filter for testing.RunTests +func allTestsFilter(pat, str string) (bool, error) { + return true, nil +} + // SuiteRequireTwice is intended to test the usage of suite.Require in two // different tests type SuiteRequireTwice struct{ Suite } From 250eaa5e06e4976625f91ca3ac9c4262ab7f8a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Mon, 4 Mar 2024 23:58:37 +0100 Subject: [PATCH 09/11] deps: fix dependency cycle with objx $ go mod edit -dropexclude=github.com/stretchr/testify@v1.8.0 -exclude=github.com/stretchr/testify@v1.8.4 $ go mod tidy See https://github.com/stretchr/objx/pull/140 --- go.mod | 2 +- go.sum | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 943798ea7..16a61cc3c 100644 --- a/go.mod +++ b/go.mod @@ -13,4 +13,4 @@ require ( // Break dependency cycle with objx. // See https://github.com/stretchr/objx/pull/140 -exclude github.com/stretchr/testify v1.8.2 +exclude github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index d42acda31..3ba5aea30 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +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/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -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/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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= From 553e822bab7044a749c7ae15f08b9e266fd56cdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Sun, 25 May 2025 22:09:43 +0200 Subject: [PATCH 10/11] go.mod: add comment about how to fix dep cycle with objx --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 16a61cc3c..f4657c9bf 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ 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.2 + github.com/stretchr/objx v0.5.2 // To avoid a cycle the version of testify used by objx should be excluded below gopkg.in/yaml.v3 v3.0.1 ) From c519b7942b86df5b70eca5687be814cd2e596417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Wed, 28 May 2025 16:08:21 +0200 Subject: [PATCH 11/11] assert.Empty: comprehensive doc of "Empty"-ness rules Document the assert.Empty rules more comprehensively. This exposes our quirks to the user to avoid wrong expectations. Add many many many more test cases that document edges cases and will allow to catch breaking changes and avoid regressions. --- assert/assertion_format.go | 16 ++++-- assert/assertion_forward.go | 32 +++++++++--- assert/assertions.go | 16 ++++-- assert/assertions_test.go | 97 ++++++++++++++++++++++++++++++++++++- require/require.go | 32 +++++++++--- require/require_forward.go | 32 +++++++++--- 6 files changed, 192 insertions(+), 33 deletions(-) diff --git a/assert/assertion_format.go b/assert/assertion_format.go index 714404125..c592f6ad5 100644 --- a/assert/assertion_format.go +++ b/assert/assertion_format.go @@ -50,10 +50,19 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // assert.Emptyf(t, obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -595,8 +604,7 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str return NotElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if assert.NotEmptyf(t, obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go index 38e253ebb..58db92845 100644 --- a/assert/assertion_forward.go +++ b/assert/assertion_forward.go @@ -92,10 +92,19 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st return ElementsMatchf(a.t, listA, listB, msg, args...) } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Empty(obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -103,10 +112,19 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { return Empty(a.t, object, msgAndArgs...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Emptyf(obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1182,8 +1200,7 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg return NotElementsMatchf(a.t, listA, listB, msg, args...) } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if a.NotEmpty(obj) { // assert.Equal(t, "two", obj[1]) @@ -1195,8 +1212,7 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo return NotEmpty(a.t, object, msgAndArgs...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if a.NotEmptyf(obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) diff --git a/assert/assertions.go b/assert/assertions.go index 9913ac2f0..46582060d 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -773,10 +773,19 @@ func isEmpty(object interface{}) bool { } } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // assert.Empty(t, obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := isEmpty(object) if !pass { @@ -789,8 +798,7 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return pass } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if assert.NotEmpty(t, obj) { // assert.Equal(t, "two", obj[1]) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 955188d5a..d30eef60d 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1728,24 +1728,119 @@ func Test_isEmpty(t *testing.T) { True(t, isEmpty("")) True(t, isEmpty(nil)) + True(t, isEmpty(error(nil))) + True(t, isEmpty((*int)(nil))) + True(t, isEmpty((*string)(nil))) + True(t, isEmpty(new(string))) True(t, isEmpty([]string{})) + True(t, isEmpty([]string(nil))) + True(t, isEmpty([]byte(nil))) + True(t, isEmpty([]byte{})) + True(t, isEmpty([]byte(""))) + True(t, isEmpty([]bool(nil))) + True(t, isEmpty([]bool{})) + True(t, isEmpty([]interface{}(nil))) + True(t, isEmpty([]interface{}{})) + True(t, isEmpty(struct{}{})) + True(t, isEmpty(&struct{}{})) + True(t, isEmpty(struct{ A int }{A: 0})) + True(t, isEmpty(struct{ a int }{a: 0})) + True(t, isEmpty(struct { + a int + B int + }{a: 0, B: 0})) True(t, isEmpty(0)) + True(t, isEmpty(int(0))) + True(t, isEmpty(int8(0))) + True(t, isEmpty(int16(0))) + True(t, isEmpty(uint16(0))) True(t, isEmpty(int32(0))) + True(t, isEmpty(uint32(0))) True(t, isEmpty(int64(0))) + True(t, isEmpty(uint64(0))) + True(t, isEmpty('\u0000')) // rune => int32 + True(t, isEmpty(float32(0))) + True(t, isEmpty(float64(0))) + True(t, isEmpty(0i)) // complex + True(t, isEmpty(0.0i)) // complex True(t, isEmpty(false)) + True(t, isEmpty(new(bool))) True(t, isEmpty(map[string]string{})) + True(t, isEmpty(map[string]string(nil))) True(t, isEmpty(new(time.Time))) True(t, isEmpty(time.Time{})) True(t, isEmpty(make(chan struct{}))) - True(t, isEmpty([1]int{})) + True(t, isEmpty(chan struct{}(nil))) + True(t, isEmpty(chan<- struct{}(nil))) + True(t, isEmpty(make(chan struct{}))) + True(t, isEmpty(make(chan<- struct{}))) + True(t, isEmpty(make(chan struct{}, 1))) + True(t, isEmpty(make(chan<- struct{}, 1))) + True(t, isEmpty([1]int{0})) + True(t, isEmpty([2]int{0, 0})) + True(t, isEmpty([8]int{})) + True(t, isEmpty([...]int{7: 0})) + True(t, isEmpty([...]bool{false, false})) + True(t, isEmpty(errors.New(""))) // BEWARE + True(t, isEmpty([]error{})) + True(t, isEmpty([]error(nil))) + True(t, isEmpty(&[1]int{0})) + True(t, isEmpty(&[2]int{0, 0})) False(t, isEmpty("something")) False(t, isEmpty(errors.New("something"))) False(t, isEmpty([]string{"something"})) False(t, isEmpty(1)) + False(t, isEmpty(int(1))) + False(t, isEmpty(uint(1))) + False(t, isEmpty(byte(1))) + False(t, isEmpty(int8(1))) + False(t, isEmpty(uint8(1))) + False(t, isEmpty(int16(1))) + False(t, isEmpty(uint16(1))) + False(t, isEmpty(int32(1))) + False(t, isEmpty(uint32(1))) + False(t, isEmpty(int64(1))) + False(t, isEmpty(uint64(1))) + False(t, isEmpty('A')) // rune => int32 False(t, isEmpty(true)) + False(t, isEmpty(1.0)) + False(t, isEmpty(1i)) // complex + False(t, isEmpty([]byte{0})) // elements values are ignored for slices + False(t, isEmpty([]byte{0, 0})) // elements values are ignored for slices + False(t, isEmpty([]string{""})) // elements values are ignored for slices + False(t, isEmpty([]string{"a"})) // elements values are ignored for slices + False(t, isEmpty([]bool{false})) // elements values are ignored for slices + False(t, isEmpty([]bool{true})) // elements values are ignored for slices + False(t, isEmpty([]error{errors.New("xxx")})) + False(t, isEmpty([]error{nil})) // BEWARE + False(t, isEmpty([]error{errors.New("")})) // BEWARE False(t, isEmpty(map[string]string{"Hello": "World"})) + False(t, isEmpty(map[string]string{"": ""})) + False(t, isEmpty(map[string]string{"foo": ""})) + False(t, isEmpty(map[string]string{"": "foo"})) False(t, isEmpty(chWithValue)) + False(t, isEmpty([1]bool{true})) + False(t, isEmpty([2]bool{false, true})) + False(t, isEmpty([...]bool{10: true})) + False(t, isEmpty([]int{0})) + False(t, isEmpty([]int{42})) False(t, isEmpty([1]int{42})) + False(t, isEmpty([2]int{0, 42})) + False(t, isEmpty(&[1]int{42})) + False(t, isEmpty(&[2]int{0, 42})) + False(t, isEmpty([1]*int{new(int)})) // array elements must be the zero value, not any Empty value + False(t, isEmpty(struct{ A int }{A: 42})) + False(t, isEmpty(struct{ a int }{a: 42})) + False(t, isEmpty(struct{ a *int }{a: new(int)})) // fields must be the zero value, not any Empty value + False(t, isEmpty(struct{ a []int }{a: []int{}})) // fields must be the zero value, not any Empty value + False(t, isEmpty(struct { + a int + B int + }{a: 0, B: 42})) + False(t, isEmpty(struct { + a int + B int + }{a: 42, B: 0})) } func TestEmpty(t *testing.T) { diff --git a/require/require.go b/require/require.go index 6cd133417..2d02f9bce 100644 --- a/require/require.go +++ b/require/require.go @@ -117,10 +117,19 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string t.FailNow() } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // require.Empty(t, obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -131,10 +140,19 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { t.FailNow() } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // require.Emptyf(t, obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1495,8 +1513,7 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str t.FailNow() } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if require.NotEmpty(t, obj) { // require.Equal(t, "two", obj[1]) @@ -1511,8 +1528,7 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { t.FailNow() } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if require.NotEmptyf(t, obj, "error message %s", "formatted") { // require.Equal(t, "two", obj[1]) diff --git a/require/require_forward.go b/require/require_forward.go index f6192dfea..e6f7e9446 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -93,10 +93,19 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st ElementsMatchf(a.t, listA, listB, msg, args...) } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Empty(obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -104,10 +113,19 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { Empty(a.t, object, msgAndArgs...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Emptyf(obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1183,8 +1201,7 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg NotElementsMatchf(a.t, listA, listB, msg, args...) } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if a.NotEmpty(obj) { // assert.Equal(t, "two", obj[1]) @@ -1196,8 +1213,7 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { NotEmpty(a.t, object, msgAndArgs...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if a.NotEmptyf(obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1])