TL;DR
Unit-testing async code in Go? Drop in a mock that just closes a sync.WaitGroup, then wrap the wait in a select with time.After. If the goroutine never returns, the test fails quickly instead of hanging your CI.
Your function fires a goroutine — maybe it pushes to Kafka or hits an API. In a test you don’t care about the payload; you care that the goroutine actually ran. And if it deadlocks, you want to know fast.
package main
import (
"sync"
"testing"
"time"
)
type Actor interface {
Perform()
}
func Cinema(a Actor) {
go a.Perform()
}
type mockActor struct{ wg *sync.WaitGroup }
func (m mockActor) Perform() {
m.wg.Done()
}
func TestCinema(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
Cinema(mockActor{wg: &wg})
done := make(chan struct{})
go func() {
defer close(done)
wg.Wait()
}()
select {
case <-done:
case <-time.After(500 * time.Millisecond):
t.Fatalf("timeout: Perform did not finish in 500 ms")
}
}
What’s happening
Bump the WaitGroup, call the code under test.
The mock fires inside the goroutine and calls wg.Done()—proof the path was hit.
A helper goroutine converts wg.Wait() into a channel you can watch in a select.
If the deadline expires first, the test explodes instead of blocking your pipeline.
For quick unit tests, this WaitGroup-plus-timeout trick hits the sweet spot.