How to Test Goroutines in Go: Simple Guide with Examples
To test
goroutines in Go, use synchronization tools like sync.WaitGroup or channels to wait for goroutines to finish before asserting results. This ensures your test waits for concurrent code to complete and captures its output correctly.Syntax
Testing goroutines requires waiting for them to finish before checking results. The common pattern uses sync.WaitGroup to track active goroutines and Done() to signal completion. You start goroutines with go keyword and call Wait() to block until all are done.
go
var wg sync.WaitGroup wg.Add(1) // Tell WaitGroup to wait for 1 goroutine // Start goroutine go func() { defer wg.Done() // Signal goroutine done // goroutine work here }() wg.Wait() // Wait for all goroutines to finish
Example
This example shows how to test a function that runs a goroutine to double a number and send the result on a channel. The test waits for the goroutine to finish and checks the output.
go
package main import ( "sync" "testing" ) func doubleAsync(n int, wg *sync.WaitGroup, result chan<- int) { defer wg.Done() result <- n * 2 } func TestDoubleAsync(t *testing.T) { var wg sync.WaitGroup result := make(chan int, 1) wg.Add(1) go doubleAsync(5, &wg, result) wg.Wait() close(result) val := <-result if val != 10 { t.Errorf("Expected 10, got %d", val) } }
Common Pitfalls
- Not waiting for goroutines to finish causes tests to pass or fail unpredictably.
- Forgetting to close channels can cause deadlocks.
- Using unbuffered channels without proper synchronization can block goroutines indefinitely.
- Relying on
time.Sleepto wait is unreliable and slows tests.
Always use sync.WaitGroup or channels to coordinate goroutines in tests.
go
package main import ( "testing" "time" ) // Wrong: Using sleep to wait for goroutine func TestWrong(t *testing.T) { result := 0 go func() { result = 5 }() time.Sleep(10 * time.Millisecond) // Unreliable wait if result != 5 { t.Errorf("Expected 5, got %d", result) } } // Right: Using WaitGroup import ( "sync" "testing" ) func TestRight(t *testing.T) { var wg sync.WaitGroup result := 0 wg.Add(1) go func() { defer wg.Done() result = 5 }() wg.Wait() if result != 5 { t.Errorf("Expected 5, got %d", result) } }
Quick Reference
- Use
sync.WaitGroupto wait for goroutines to finish. - Use buffered channels to avoid blocking when sending results.
- Close channels after sending to signal completion.
- Avoid
time.Sleepfor synchronization in tests. - Check results only after all goroutines complete.
Key Takeaways
Always synchronize goroutines in tests using sync.WaitGroup or channels.
Never rely on time.Sleep to wait for goroutines; it is unreliable.
Close channels properly to avoid deadlocks in tests.
Wait for all goroutines to finish before asserting test results.
Use buffered channels when sending data from goroutines to prevent blocking.