I have a codebase that is roughly as follows
type Service struct {
Repo repo // An interface that contains both FunctionOne and FunctionTwo
GoRoutineWaitgroup *sync.WaitGroup
}
func (impl *Service) MyFunction(s string) bool {
a := impl.Repo.FunctionOne()
b := impl.Repo.FunctionTwo()
fmt.Println("Executed Function One and Function two")
go impl.validateMyFunction(a,b)
return true
}
func (impl *Service) validateMyFunction(a string,b string) {
defer helpers.PanicHandler()
impl.GoRoutineWaitgroup.Add(1)
defer impl.GoRoutineWaitgroup.Done()
fmt.Println("a and b are validated")
}
And I wrote unit tests as something similar to this.
func TestMyFunction(t *testing.T) {
ms := &Service{}
test := []struct{
input string
output bool
case string
}{
{"a", true, sample}
}
}
for _, test := range tests {
t.Run(test.case, func(t *testing.T) {
mockRepo := new(mockrepo.Repo) // mockRepo contains mocks of original repo layer methods generated using mockery for testing purposes
mockRepo.On("FunctionOne")
mockRepo.On("FunctionTwo")
ms.Repo = mockRepo
op := ms.MyFunction(test.input)
assert.Equal(t, test.Output, op)
})
}
} // Please keep in mind that this is not my actual code, but just a basic structure.
All tests are successful. But when executing the command go test -v
, I saw multiple places in the code where the program panicked and gave invalid memory address or nil pointer dereference
. I checked the code in debug mode and realized that the issue is with impl.GoRoutineWaitgroup.Add(1)
in method validateMyFunction
and when I commented out go validateMyFunction(a,b)
and ran tests again, there were no panics in the logs. So How do I solve this issue? How to handle unit tests of functions where we start a goroutine from inside ( as in this case )?
You need initialize value to GoRoutineWaitgroup
field.
ms := &Service{GoRoutineWaitgroup: &sync.WaitGroup{}}
Or remove pointer from definition
type Service struct {
Repo repo
GoRoutineWaitgroup sync.WaitGroup
}
Also I do not see waiting for Wait Group in your code. Something like ms.GoRoutineWaitgroup.Wait()
and you need to move impl.GoRoutineWaitgroup.Add(1) to MyFunction
from validateMyFunction
otherwise the code in validateMyFunction
will not be called