Golang / GoLang System Architecture and Testing Interview Questions
How do you test concurrent Go code correctly — including data races and timing issues?
Concurrent code is notoriously difficult to test because bugs may only appear under specific goroutine interleavings. Go provides three essential tools: the race detector (-race), goroutine leak detection, and deterministic design.
import ( "testing" "sync" "go.uber.org/goleak" ) // Always run concurrent tests with -race // go test -race ./... // Test goroutine leak detection func TestNoGoroutineLeak(t *testing.T) { defer goleak.VerifyNone(t) // fails if goroutines remain after test ctx, cancel := context.WithCancel(context.Background()) worker := NewBackgroundWorker(ctx) worker.Start() // do some work... cancel() // signal worker to stop worker.Wait() // goleak checks that the worker goroutine actually exited } // Test shared state with concurrent access func TestCounter_ConcurrentIncrement(t *testing.T) { c := NewAtomicCounter() const goroutines = 100 const increments = 1000 var wg sync.WaitGroup wg.Add(goroutines) for i := 0; i < goroutines; i++ { go func() { defer wg.Done() for j := 0; j < increments; j++ { c.Increment() } }() } wg.Wait() expected := goroutines * increments if got := c.Value(); got != expected { t.Errorf("got %d, want %d", got, expected) } } // Test channel-based pipelines with timeout func TestPipeline_ProcessesAllItems(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() in := generateJobs([]string{"a", "b", "c"}) out := processJobs(ctx, in, 3) results := collectResults(ctx, out) if len(results) != 3 { t.Errorf("expected 3 results, got %d", len(results)) } }
Deterministic test design: avoid time.Sleep in tests to wait for goroutines — use WaitGroup, channels, or context. Sleep-based synchronisation makes tests flaky on slow CI machines. Design concurrent components so their completion is signalled through channels or sync primitives.
More Related questions...