I'm new to Go here. I am trying to test the function call inside my Go routine but it fails with the error message
Expected number of calls (8) does not match the actual number of calls (0).
My test code goes like:
package executor
import (
"testing"
"sync"
"github.com/stretchr/testify/mock"
)
type MockExecutor struct {
mock.Mock
wg sync.WaitGroup
}
func (m *MockExecutor) Execute() {
defer m.wg.Done()
}
func TestScheduleWorksAsExpected(t *testing.T) {
scheduler := GetScheduler()
executor := &MockExecutor{}
scheduler.AddExecutor(executor)
// Mock exptectations
executor.On("Execute").Return()
// Function Call
executor.wg.Add(8)
scheduler.Schedule(2, 1, 4)
executor.wg.Wait()
executor.AssertNumberOfCalls(t, "Execute", 8)
}
and my application code is:
package executor
import (
"sync"
"time"
)
type Scheduler interface {
Schedule(repeatRuns uint16, coolDown uint8, parallelRuns uint64)
AddExecutor(executor Executor)
}
type RepeatScheduler struct {
executor Executor
waitGroup sync.WaitGroup
}
func GetScheduler() Scheduler {
return &RepeatScheduler{}
}
func (r *RepeatScheduler) singleRun() {
defer r.waitGroup.Done()
r.executor.Execute()
}
func (r *RepeatScheduler) AddExecutor(executor Executor) {
r.executor = executor
}
func (r *RepeatScheduler) repeatRuns(parallelRuns uint64) {
for count := 0; count < int(parallelRuns); count += 1 {
r.waitGroup.Add(1)
go r.singleRun()
}
r.waitGroup.Wait()
}
func (r *RepeatScheduler) Schedule(repeatRuns uint16, coolDown uint8, parallelRuns uint64) {
for repeats := 0; repeats < int(repeatRuns); repeats += 1 {
r.repeatRuns(parallelRuns)
time.Sleep(time.Duration(coolDown))
}
}
Could you point out to me what I could be doing wrong here? I'm using Go 1.16.3. When I debug my code, I can see the Execute() function being called but testify is not able to register the function call
You need to call Called()
so that mock.Mock
records the fact that Execute()
has been called. As you are not worried about arguments or return values the following should resolve your issue:
func (m *MockExecutor) Execute() {
defer m.wg.Done()
m.Called()
}
However I note that the way your test is currently written this test may not accomplish what you want. This is because:
executor.wg.Wait()
(which will wait until the function has been called the expected number of times) before calling executor.AssertNumberOfCalls
so your test will never complete if Execute()
is not called at least the expected number of times (wg.Wait()
will block forever).m.Called()
has been called the expected number of times there is a race condition (if executor
is still be running there is a race between executor.AssertNumberOfCalls
and the next m.Called()
). If wg.Done()
does get called an extra time you will get a panic (which I guess you could consider a fail!) but I'd probably simplify the test a bit:scheduler.Schedule(2, 1, 4)
time.Sleep(time.Millisecond) // Wait long enough that all executions are guaranteed to have completed (should be quick as Schedule waits for go routines to end)
executor.AssertNumberOfCalls(t, "Execute", 8)