Unit testing of a function that starts a go routine inside it

Issue

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 )?

Solution

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

Answered By – outdead

Answer Checked By – Katrina (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.