diff --git a/mock/mock.go b/mock/mock.go index e150c6dd4..879b784c3 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -249,14 +249,15 @@ func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, * return -1, nil } -func (m *Mock) findClosestCall(method string, arguments ...interface{}) (bool, *Call) { - diffCount := 0 +func (m *Mock) findClosestCall(method string, arguments ...interface{}) (*Call, string) { + var diffCount, tempDiffCount int var closestCall *Call + var errInfo string for _, call := range m.expectedCalls() { if call.Method == method { - _, tempDiffCount := call.Arguments.Diff(arguments) + errInfo, tempDiffCount = call.Arguments.Diff(arguments) if tempDiffCount < diffCount || diffCount == 0 { diffCount = tempDiffCount closestCall = call @@ -265,11 +266,7 @@ func (m *Mock) findClosestCall(method string, arguments ...interface{}) (bool, * } } - if closestCall == nil { - return false, nil - } - - return true, closestCall + return closestCall, errInfo } func callString(method string, arguments Arguments, includeArgumentValues bool) string { @@ -316,6 +313,7 @@ func (m *Mock) Called(arguments ...interface{}) Arguments { // If Call.WaitFor is set, blocks until the channel is closed or receives a message. func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Arguments { m.mutex.Lock() + //TODO: could combine expected and closes in single loop found, call := m.findExpectedCall(methodName, arguments...) if found < 0 { @@ -326,11 +324,16 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen // b) the arguments are not what was expected, or // c) the developer has forgotten to add an accompanying On...Return pair. - closestFound, closestCall := m.findClosestCall(methodName, arguments...) + closestCall, mismatch := m.findClosestCall(methodName, arguments...) m.mutex.Unlock() - if closestFound { - panic(fmt.Sprintf("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\n", callString(methodName, arguments, true), callString(methodName, closestCall.Arguments, true), diffArguments(closestCall.Arguments, arguments))) + if closestCall != nil { + panic(fmt.Sprintf("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s", + callString(methodName, arguments, true), + callString(methodName, closestCall.Arguments, true), + diffArguments(closestCall.Arguments, arguments), + strings.TrimSpace(mismatch)), + ) } else { panic(fmt.Sprintf("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo())) } @@ -604,6 +607,7 @@ func (args Arguments) Is(objects ...interface{}) bool { // // Returns the diff string and number of differences found. func (args Arguments) Diff(objects []interface{}) (string, int) { + //TODO: could return string as error and nil for No difference var output = "\n" var differences int diff --git a/mock/mock_test.go b/mock/mock_test.go index b433e7af6..7d8ab2a33 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -8,9 +8,10 @@ import ( "testing" "time" + "runtime" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "runtime" ) /* @@ -1384,6 +1385,43 @@ func TestAfterTotalWaitTimeWhileExecution(t *testing.T) { } } +func TestArgumentMatcherToPrintMismatch(t *testing.T) { + defer func() { + if r := recover(); r != nil { + matchingExp := regexp.MustCompile( + `\s+mock: Unexpected Method Call\s+-*\s+GetTime\(int\)\s+0: 1\s+The closest call I have is:\s+GetTime\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(int=1\) not matched by func\(int\) bool`) + assert.Regexp(t, matchingExp, r) + } + }() + + m := new(timer) + m.On("GetTime", MatchedBy(func(i int) bool { return false })).Return("SomeTime").Once() + + res := m.GetTime(1) + require.Equal(t, "SomeTime", res) + m.AssertExpectations(t) +} + +func TestClosestCallMismatchedArgumentValueInformation(t *testing.T) { + defer func() { + if r := recover(); r != nil { + matchingExp := regexp.MustCompile(unexpectedCallRegex(`GetTime(int)`, "1", "999", `0: FAIL: %!s(int=1) != %!s(int=999)`)) + assert.Regexp(t, matchingExp, r) + } + }() + + m := new(timer) + m.On("GetTime", 999).Return("SomeTime").Once() + + _ = m.GetTime(1) +} + +func unexpectedCallRegex(method, calledArg, expectedArg, diff string) string { + rMethod := regexp.QuoteMeta(method) + return fmt.Sprintf(`\s+mock: Unexpected Method Call\s+-*\s+%s\s+0: %s\s+The closest call I have is:\s+%s\s+0: %s\s+Diff: %s`, + rMethod, regexp.QuoteMeta(calledArg), rMethod, regexp.QuoteMeta(expectedArg), regexp.QuoteMeta(diff)) +} + func ConcurrencyTestMethod(m *Mock) { m.Called() }