🔬 Testing – Context, Timeouts & Cancellation


🎯 Objective

Learn how to test:

  • Context-aware functions
  • Timeout behavior
  • Cancellation flows
  • Concurrent operations safely

🧠 Why Testing Context is Important

In real systems:

  • Timeouts prevent system overload
  • Cancellation avoids resource leaks
  • Bugs often appear in edge cases

👉 Testing ensures:

  • Your code stops when it should
  • Your system is resilient under stress

🧪 1. Basic Context Testing

Example Function

```go id=”l6y7qv” func doWork(ctx context.Context) error { select { case <-time.After(2 * time.Second): return nil case <-ctx.Done(): return ctx.Err() } }


---

### Test Case

```go id="pj4rj6"
func TestDoWork_Success(t *testing.T) {
	ctx := context.Background()

	err := doWork(ctx)

	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
}

⏱ 2. Testing Timeout Behavior

```go id=”qiv8fb” func TestDoWork_Timeout(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel()

err := doWork(ctx)

if err == nil {
	t.Errorf("expected timeout error, got nil")
}

if err != context.DeadlineExceeded {
	t.Errorf("expected DeadlineExceeded, got %v", err)
} } ```

🧠 Key Learning:

  • Always verify specific error type

🚫 3. Testing Cancellation

```go id=”7j8j0x” func TestDoWork_Cancel(t *testing.T) { ctx, cancel := context.WithCancel(context.Background())

cancel() // cancel immediately

err := doWork(ctx)

if err != context.Canceled {
	t.Errorf("expected Canceled error, got %v", err)
} } ```

⚡ 4. Testing Fast Execution (Avoid Slow Tests)

❌ Bad:

```go id=”xvq6c7” time.Sleep(5 * time.Second)


✅ Good:

* Use shorter durations in tests
* Use dependency injection if needed

```go id="a8d3zg"
func doWorkWithDelay(ctx context.Context, delay time.Duration) error {
	select {
	case <-time.After(delay):
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

🔁 5. Table-Driven Tests

```go id=”94chhc” func TestDoWork_TableDriven(t *testing.T) { tests := []struct { name string timeout time.Duration expectErr bool }{ {“no-timeout”, 3 * time.Second, false}, {“timeout”, 1 * time.Second, true}, }

for _, tt := range tests {
	t.Run(tt.name, func(t *testing.T) {
		ctx, cancel := context.WithTimeout(context.Background(), tt.timeout)
		defer cancel()

		err := doWork(ctx)

		if tt.expectErr && err == nil {
			t.Errorf("expected error, got nil")
		}
	})
} } ```

🧠 Key Learning:

  • Scalable testing pattern
  • Clean and reusable

🧵 6. Testing Concurrent Code Safely

Example

```go id=”3dtpb0” func worker(ctx context.Context, done chan bool) { select { case <-time.After(2 * time.Second): done <- true case <-ctx.Done(): done <- false } }


---

### Test

```go id="r2a1nm"
func TestWorker_Cancel(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	done := make(chan bool)

	go worker(ctx, done)

	cancel()

	result := <-done

	if result != false {
		t.Errorf("expected false, got true")
	}
}

🧠 Key Learning:

  • Always synchronize goroutines in tests
  • Avoid race conditions

🧪 7. Using go test -race

Run:

go test -race ./...

👉 Detects:

  • Race conditions
  • Unsafe concurrent access

⚠️ Common Testing Mistakes

  • ❌ Using long sleeps (slow tests)
  • ❌ Not cancelling context
  • ❌ Ignoring error values
  • ❌ Not testing cancellation paths
  • ❌ Flaky timing-based tests

🧠 Best Practices

  • Keep tests fast (<1 second ideally)
  • Always test both success and failure paths
  • Use table-driven tests
  • Inject dependencies (timeouts, delays)
  • Prefer deterministic behavior

🚀 Final Takeaways

  • Context testing is about control and predictability
  • Always verify:

    • Timeout
    • Cancellation
    • Success path
  • Good tests = production reliability

👉 Next: Assessment



This site uses Just the Docs, a documentation theme for Jekyll.