Skip to content

Commit

Permalink
Displaying mismatch information of arguments diff while panic for clo…
Browse files Browse the repository at this point in the history
…sest function call. closes #556
  • Loading branch information
Dinesh Kumar authored and ernesto-jimenez committed Mar 18, 2018
1 parent 6efb0c4 commit 921da25
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 12 deletions.
27 changes: 16 additions & 11 deletions mock/mock.go
Expand Up @@ -249,27 +249,25 @@ 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 int
var closestCall *Call
var err 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
err = errInfo
}

}
}

if closestCall == nil {
return false, nil
}

return true, closestCall
return closestCall, err
}

func callString(method string, arguments Arguments, includeArgumentValues bool) string {
Expand Down Expand Up @@ -316,6 +314,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 {
Expand All @@ -326,11 +325,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()))
}
Expand Down Expand Up @@ -627,6 +631,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
Expand Down
55 changes: 54 additions & 1 deletion mock/mock_test.go
Expand Up @@ -8,9 +8,10 @@ import (
"testing"
"time"

"runtime"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"runtime"
)

/*
Expand Down Expand Up @@ -1384,6 +1385,58 @@ 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 TestClosestCallMismatchedArgumentInformationShowsTheClosest(t *testing.T) {
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`, `0: PASS: %!s\(int=1\) == %!s\(int=1\)\s+1: PASS: %!s\(int=1\) == %!s\(int=1\)\s+2: FAIL: %!s\(int=2\) != %!s\(int=1\)`))
assert.Regexp(t, matchingExp, r)
}
}()

m := new(TestExampleImplementation)
m.On("TheExampleMethod", 1, 1, 1).Return(1, nil).Once()
m.On("TheExampleMethod", 2, 2, 2).Return(2, nil).Once()

m.TheExampleMethod(1, 1, 2)
}

func TestClosestCallMismatchedArgumentValueInformation(t *testing.T) {
defer func() {
if r := recover(); r != nil {
matchingExp := regexp.MustCompile(unexpectedCallRegex(`GetTime(int)`, "0: 1", "0: 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+%s\s+The closest call I have is:\s+%s\s+%s\s+Diff: %s`,
rMethod, calledArg, rMethod, expectedArg, diff)
}

func ConcurrencyTestMethod(m *Mock) {
m.Called()
}

0 comments on commit 921da25

Please sign in to comment.