Search code examples
unit-testinggotestify

How to validate that a method is called in a separate go routine


While writing unit test for a method in go, I am stumped at a problem. First, code snippet under test:

func MehodToBeTested(e Entity) {
  go saveAudit(e)

 //do something on which assertions can be done
}

Entity can be mocked. in the saveAudit method, Entity.Save method is called. In my UT, i want to assert that Entity.Save method is called once. Following is my current UT :

func TestMethod(t *testing.T) {
  var mock = &mockEntity{}
  mock.On("Save").Return(nil)

  //make call to func under test
  MethodToBeTested(mock)

  // Assert that Save is called on Entity
  mock.AssertNumberOfCalls(t, "Save",1)
}

This is giving error saying : Expected number of calls (1) does not match the actual number of calls (0) since the actual call is happening in another go routine. How can i test this?


Solution

  • I use the same technique. Just wait for goroutine end. Very likely it’s not yet set.

    Also, I recommend to run such tests with the race-condition detector. It helps to catch such situations. Then you can add some synchronization to the tests to make them reliable.

    Example from my test. A tested function should check in parallel that both web pages contain the specified string. So test should check that tested function has visited both pages

    UPDATE: incorrect test was attached. Fixed.

    func TestCheckSites_TwoSlowHandlers_BothContain(t *testing.T) {
        var config = GetConfig()
        var v1, v2 bool
        var wg sync.WaitGroup
        wg.Add(2)
        handler1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            defer wg.Done()
            v1 = true
            time.Sleep(2 * config.Http.Timeout) // Use double HTTP_TIMEOUT
            io.WriteString(w, "Present")
        })
        ts1 := httptest.NewServer(handler1)
        defer ts1.Close()
    
        handler2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            defer wg.Done()
            v2 = true
            time.Sleep(2 * config.Http.Timeout)
            io.WriteString(w, "Present")
        })
        ts2 := httptest.NewServer(handler2)
        defer ts2.Close()
    
        result, err := checkSites([]string{ts1.URL, ts2.URL}, "Present")
        assert.Equal(t, nil, err, "Error should be nil")
        assert.Contains(t, []string{""}, result, "Should be empty string")
        //assert.(t, ts1.URL, result, "Should first or second empty string")
        wg.Wait()
        assert.Equal(t, true, v1, "First server should be visited")
        assert.Equal(t, true, v2, "Second server should be visited")
    }