Skip to content

mock.IsMethodCallable: fix #1718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 5 additions & 33 deletions mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -712,45 +712,17 @@ func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...inter
return true
}

// IsMethodCallable checking that the method can be called
// If the method was called more than `Repeatability` return false
func (m *Mock) IsMethodCallable(t TestingT, methodName string, arguments ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
// IsMethodCallable returns true if the methodName and arguments are currently expected
Copy link
Collaborator

@dolmen dolmen May 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// IsMethodCallable returns true if the methodName and arguments are currently expected
// IsMethodCallable returns true if the given call (methodName with arguments) is currently expected.

func (m *Mock) IsMethodCallable(_ TestingT, methodName string, arguments ...interface{}) bool {
m.mutex.Lock()
defer m.mutex.Unlock()

for _, v := range m.ExpectedCalls {
if v.Method != methodName {
continue
}
if len(arguments) != len(v.Arguments) {
continue
}
if v.Repeatability < v.totalCalls {
continue
}
if isArgsEqual(v.Arguments, arguments) {
return true
}
index, _ := m.findExpectedCall(methodName, arguments...)
if index != -1 {
return true
}
return false
Comment on lines +720 to 723
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if index != -1 {
return true
}
return false
return index != -1

}

// isArgsEqual compares arguments
func isArgsEqual(expected Arguments, args []interface{}) bool {
if len(expected) != len(args) {
return false
}
for i, v := range args {
if !reflect.DeepEqual(expected[i], v) {
return false
}
}
return true
}

func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool {
for _, call := range m.calls() {
if call.Method == methodName {
Expand Down
69 changes: 41 additions & 28 deletions mock/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1712,35 +1712,48 @@ func Test_Mock_AssertNotCalled(t *testing.T) {
}

func Test_Mock_IsMethodCallable(t *testing.T) {
var mockedService = new(TestExampleImplementation)

arg := []Call{{Repeatability: 1}, {Repeatability: 2}}
arg2 := []Call{{Repeatability: 1}, {Repeatability: 1}}
arg3 := []Call{{Repeatability: 1}, {Repeatability: 1}}

mockedService.On("Test_Mock_IsMethodCallable", arg2).Return(true).Twice()

assert.False(t, mockedService.IsMethodCallable(t, "Test_Mock_IsMethodCallable", arg))
assert.True(t, mockedService.IsMethodCallable(t, "Test_Mock_IsMethodCallable", arg2))
assert.True(t, mockedService.IsMethodCallable(t, "Test_Mock_IsMethodCallable", arg3))

mockedService.MethodCalled("Test_Mock_IsMethodCallable", arg2)
mockedService.MethodCalled("Test_Mock_IsMethodCallable", arg2)

assert.False(t, mockedService.IsMethodCallable(t, "Test_Mock_IsMethodCallable", arg2))
}

func TestIsArgsEqual(t *testing.T) {
var expected = Arguments{5, 3, 4, 6, 7, 2}

// Copy elements 1 to 5
args := append(([]interface{})(nil), expected[1:]...)
args[2] = expected[1]
assert.False(t, isArgsEqual(expected, args))
mock := new(TestExampleImplementation)

tests := []struct {
name string
expectedCalls []*Call
method string
args Arguments
expected bool
}{
{
name: "Method is expected and has repeatability when called should return true",
expectedCalls: []*Call{
{Method: "TestMethod", Repeatability: 1, Arguments: Arguments{"TestArg"}},
},
method: "TestMethod",
args: Arguments{"TestArg"},
expected: true,
},
{
name: "Method is not expected when called should return false",
expectedCalls: []*Call{},
method: "TestMethod",
args: Arguments{"TestArg"},
expected: false,
},
{
name: "Method is expected and has no repeatability when called should return false",
expectedCalls: []*Call{
{Method: "TestMethod", Repeatability: -1, Arguments: Arguments{"TestArg"}},
},
method: "TestMethod",
args: Arguments{"TestArg"},
expected: false,
},
}

// Clone
arr := append(([]interface{})(nil), expected...)
assert.True(t, isArgsEqual(expected, arr))
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mock.Mock.ExpectedCalls = tt.expectedCalls
assert.Equal(t, tt.expected, mock.IsMethodCallable(t, tt.method, tt.args...))
})
}
}

func Test_Mock_AssertOptional(t *testing.T) {
Expand Down