Skip to content

Commit 564214b

Browse files
committed
mock.AnythingOfType fix panic when matching nil type.
AnythingOfType would panic if the actual arg value was of type nil. Handle the nil type case before calling reflect.Type.String. Because (<nil>=<nil>) looks confusing, change the failure message value format to the more idiomatic (%T)(%v). In order to better test the failure message, change the mockTestingT to capture the messages passed to it.
1 parent feb1324 commit 564214b

File tree

2 files changed

+119
-11
lines changed

2 files changed

+119
-11
lines changed

mock/mock.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,8 @@ type AnythingOfTypeArgument = anythingOfTypeArgument
808808
type anythingOfTypeArgument string
809809

810810
// AnythingOfType returns a special value containing the
811-
// name of the type to check for. The type name will be matched against the type name returned by [reflect.Type.String].
811+
// name of the type to check for. The type name will be matched against the type
812+
// name returned by [reflect.Type.String], or against "<nil>" for nil type .
812813
//
813814
// Used in Diff and Assert.
814815
//
@@ -1007,10 +1008,16 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
10071008
switch expected := expected.(type) {
10081009
case anythingOfTypeArgument:
10091010
// type checking
1010-
if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) {
1011+
actualTypeName := "<nil>"
1012+
actualTypeString := "<nil>"
1013+
if actual != nil {
1014+
actualTypeName = reflect.TypeOf(actual).Name()
1015+
actualTypeString = reflect.TypeOf(actual).String()
1016+
}
1017+
if actualTypeName != string(expected) && actualTypeString != string(expected) {
10111018
// not match
10121019
differences++
1013-
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt)
1020+
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, actualTypeName, actualFmt)
10141021
}
10151022
case *IsTypeArgument:
10161023
actualT := reflect.TypeOf(actual)

mock/mock_test.go

Lines changed: 109 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,20 +131,21 @@ func (i *TestExampleImplementation) TheExampleMethodFuncType(fn ExampleFuncType)
131131

132132
// MockTestingT mocks a test struct
133133
type MockTestingT struct {
134-
logfCount, errorfCount, failNowCount int
134+
logfMessages, errorfMessages []string
135+
failNowCount int
135136
}
136137

137138
// Helper is like [testing.T.Helper] but does nothing.
138139
func (MockTestingT) Helper() {}
139140

140141
const mockTestingTFailNowCalled = "FailNow was called"
141142

142-
func (m *MockTestingT) Logf(string, ...interface{}) {
143-
m.logfCount++
143+
func (m *MockTestingT) Logf(format string, a ...interface{}) {
144+
m.logfMessages = append(m.logfMessages, fmt.Sprintf(format, a...))
144145
}
145146

146-
func (m *MockTestingT) Errorf(string, ...interface{}) {
147-
m.errorfCount++
147+
func (m *MockTestingT) Errorf(format string, a ...interface{}) {
148+
m.errorfMessages = append(m.errorfMessages, fmt.Sprintf(format, a...))
148149
}
149150

150151
// FailNow mocks the FailNow call.
@@ -324,7 +325,7 @@ func TestMock_WithTest(t *testing.T) {
324325
mockedService.TheExampleMethod(1, 2, 3)
325326

326327
// Assert that Errorf and FailNow were not called
327-
assert.Equal(t, 0, mockedTest.errorfCount)
328+
assert.Equal(t, 0, len(mockedTest.errorfMessages))
328329
assert.Equal(t, 0, mockedTest.failNowCount)
329330

330331
// Test that on unexpected call, the mocked test was called to fail the test
@@ -334,7 +335,7 @@ func TestMock_WithTest(t *testing.T) {
334335
})
335336

336337
// Assert that Errorf and FailNow were called once
337-
assert.Equal(t, 1, mockedTest.errorfCount)
338+
assert.Equal(t, 1, len(mockedTest.errorfMessages))
338339
assert.Equal(t, 1, mockedTest.failNowCount)
339340
}
340341

@@ -2465,5 +2466,105 @@ func TestIssue1785ArgumentWithMutatingStringer(t *testing.T) {
24652466
func TestIssue1227AssertExpectationsForObjectsWithMock(t *testing.T) {
24662467
mockT := &MockTestingT{}
24672468
AssertExpectationsForObjects(mockT, Mock{})
2468-
assert.Equal(t, 1, mockT.errorfCount)
2469+
assert.Equal(t, 1, len(mockT.errorfMessages))
2470+
}
2471+
2472+
func TestIssue1209AnythingOfTypeNilAsString(t *testing.T) {
2473+
t.Parallel()
2474+
mockT := &MockTestingT{}
2475+
m := &Mock{}
2476+
m.Test(mockT)
2477+
m.On("Fn", AnythingOfType("string")).Return()
2478+
assert.PanicsWithValue(t, mockTestingTFailNowCalled, func() {
2479+
m.MethodCalled("Fn", nil)
2480+
})
2481+
require.Len(t, mockT.errorfMessages, 1)
2482+
require.Contains(t, mockT.errorfMessages[0], "Diff: 0: FAIL: type string != type <nil> - (<nil>=<nil>)")
2483+
}
2484+
2485+
func TestIssue1209AnythingOfTypeStringAsNil(t *testing.T) {
2486+
t.Parallel()
2487+
mockT := &MockTestingT{}
2488+
m := &Mock{}
2489+
m.Test(mockT)
2490+
m.On("Fn", AnythingOfType("<nil>")).Return()
2491+
assert.PanicsWithValue(t, mockTestingTFailNowCalled, func() {
2492+
m.MethodCalled("Fn", "")
2493+
})
2494+
require.Len(t, mockT.errorfMessages, 1)
2495+
require.Contains(t, mockT.errorfMessages[0], "Diff: 0: FAIL: type <nil> != type string - (string=)")
2496+
}
2497+
2498+
func TestIssue1209AnythingOfTypeNilAsNil(t *testing.T) {
2499+
t.Parallel()
2500+
mockT := &MockTestingT{}
2501+
m := &Mock{}
2502+
m.Test(mockT)
2503+
m.On("Fn", AnythingOfType("<nil>")).Return()
2504+
assert.NotPanics(t, func() {
2505+
m.MethodCalled("Fn", nil)
2506+
})
2507+
}
2508+
2509+
func TestIssue1209AnythingOfTypeStringAsEmptyString(t *testing.T) {
2510+
t.Parallel()
2511+
mockT := &MockTestingT{}
2512+
m := &Mock{}
2513+
m.Test(mockT)
2514+
m.On("Fn", AnythingOfType("")).Return()
2515+
assert.PanicsWithValue(t, mockTestingTFailNowCalled, func() {
2516+
m.MethodCalled("Fn", "my lovely string")
2517+
})
2518+
require.Len(t, mockT.errorfMessages, 1)
2519+
require.Contains(t, mockT.errorfMessages[0], "Diff: 0: FAIL: type != type string - (string=my lovely string)")
2520+
}
2521+
2522+
func TestIssue1209AnythingOfTypeNilAsEmptyString(t *testing.T) {
2523+
t.Parallel()
2524+
mockT := &MockTestingT{}
2525+
m := &Mock{}
2526+
m.Test(mockT)
2527+
m.On("Fn", AnythingOfType("")).Return()
2528+
assert.PanicsWithValue(t, mockTestingTFailNowCalled, func() {
2529+
m.MethodCalled("Fn", nil)
2530+
})
2531+
require.Len(t, mockT.errorfMessages, 1)
2532+
require.Contains(t, mockT.errorfMessages[0], "Diff: 0: FAIL: type != type <nil> - (<nil>=<nil>)")
2533+
}
2534+
2535+
func TestIssue1209IsTypeNilAsString(t *testing.T) {
2536+
t.Parallel()
2537+
mockT := &MockTestingT{}
2538+
m := &Mock{}
2539+
m.Test(mockT)
2540+
m.On("Fn", IsType("")).Return()
2541+
assert.PanicsWithValue(t, mockTestingTFailNowCalled, func() {
2542+
m.MethodCalled("Fn", nil)
2543+
})
2544+
require.Len(t, mockT.errorfMessages, 1)
2545+
require.Contains(t, mockT.errorfMessages[0], "Diff: 0: FAIL: type string != type <nil> - (<nil>=<nil>)")
2546+
}
2547+
2548+
func TestIssue1209IsTypeStringAsNil(t *testing.T) {
2549+
t.Parallel()
2550+
mockT := &MockTestingT{}
2551+
m := &Mock{}
2552+
m.Test(mockT)
2553+
m.On("Fn", IsType(nil)).Return()
2554+
assert.PanicsWithValue(t, mockTestingTFailNowCalled, func() {
2555+
m.MethodCalled("Fn", "")
2556+
})
2557+
require.Len(t, mockT.errorfMessages, 1)
2558+
require.Contains(t, mockT.errorfMessages[0], "Diff: 0: FAIL: type <nil> != type string - (string=)")
2559+
}
2560+
2561+
func TestIssue1209IsTypeNilAsNil(t *testing.T) {
2562+
t.Parallel()
2563+
mockT := &MockTestingT{}
2564+
m := &Mock{}
2565+
m.Test(mockT)
2566+
m.On("Fn", IsType(nil)).Return()
2567+
assert.NotPanics(t, func() {
2568+
m.MethodCalled("Fn", nil)
2569+
})
24692570
}

0 commit comments

Comments
 (0)