From ee0d3ee118f7a61d2b2cbb5c170938ca54d7b1eb Mon Sep 17 00:00:00 2001 From: AdamMagued Date: Fri, 2 Oct 2026 14:51:37 +0000 Subject: [PATCH] perf(mock): avoid copying call slice in AssertNumberOfCalls AssertNumberOfCalls previously invoked m.calls(), which creates a defensive clone of the entire m.Calls slice before iterating over it to count matching method calls. AssertNumberOfCalls already acquires and holds m.mutex across its execution and only reads call.Method. Iterating directly over m.Calls under the mutex lock eliminates redundant heap allocation and slice copying proportional to the call history length. Fixes #1957 --- mock/mock.go | 2 +- mock/mock_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/mock/mock.go b/mock/mock.go index 746151e1b..2f2cfc8cb 100644 --- a/mock/mock.go +++ b/mock/mock.go @@ -666,7 +666,7 @@ func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls m.mutex.Lock() defer m.mutex.Unlock() var actualCalls int - for _, call := range m.calls() { + for _, call := range m.Calls { if call.Method == methodName { actualCalls++ } diff --git a/mock/mock_test.go b/mock/mock_test.go index c30f526c4..af3f21a38 100644 --- a/mock/mock_test.go +++ b/mock/mock_test.go @@ -1742,6 +1742,37 @@ func Test_Mock_AssertNumberOfCalls(t *testing.T) { mockedService.Called(1, 2, 3) assert.True(t, mockedService.AssertNumberOfCalls(t, "Test_Mock_AssertNumberOfCalls", 2)) + tt := new(testing.T) + assert.False(t, mockedService.AssertNumberOfCalls(tt, "Test_Mock_AssertNumberOfCalls", 3)) + assert.True(t, mockedService.AssertNumberOfCalls(t, "Test_Mock_UncalledMethod", 0)) + assert.False(t, mockedService.AssertNumberOfCalls(tt, "Test_Mock_UncalledMethod", 1)) +} + +func Test_Mock_AssertNumberOfCalls_Allocations(t *testing.T) { + var mockedService = new(TestExampleImplementation) + mockedService.On("TheMethod").Return() + for i := 0; i < 100; i++ { + mockedService.MethodCalled("TheMethod") + } + + allocs := testing.AllocsPerRun(100, func() { + mockedService.AssertNumberOfCalls(t, "TheMethod", 100) + }) + assert.LessOrEqual(t, allocs, float64(3)) +} + +func Benchmark_Mock_AssertNumberOfCalls(b *testing.B) { + var mockedService = new(TestExampleImplementation) + mockedService.On("TheMethod").Return() + for i := 0; i < 100; i++ { + mockedService.MethodCalled("TheMethod") + } + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + mockedService.AssertNumberOfCalls(b, "TheMethod", 100) + } } func Test_Mock_AssertCalled(t *testing.T) {