/Testing Strategies
🧪

Testing Strategies

Day 2 · Production Systems · 40 min

Table-Driven Tests: The idiomatic Go testing pattern. Define inputs and expected outputs as a slice of structs, iterate and run each as a subtest.

func TestAuthorizePayment(t testing.T) {
tests := []struct {
name string
balance string
amount string
wantStatus string
wantErr bool
}{
{"sufficient balance", "500.00", "100.00", "authorized", false},
{"exact balance", "100.00", "100.00", "authorized", false},
{"insufficient balance", "50.00", "100.00", "", true},
{"zero amount", "500.00", "0.00", "", true},
{"negative amount", "500.00", "-10.00", "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t
testing.T) {
svc := NewPaymentService(mockRepo, mockChain)
result, err := svc.Authorize(ctx, tt.balance, tt.amount)
if (err != nil) != tt.wantErr {
t.Fatalf("err = %v, wantErr = %v", err, tt.wantErr)
}
if !tt.wantErr && result.Status != tt.wantStatus {
t.Errorf("status = %s, want %s", result.Status, tt.wantStatus)
}
})
}
}

Mocks via Interfaces: Define behavior as interfaces, inject mock implementations in tests.

type BlockchainClient interface {
SendTransaction(ctx context.Context, tx types.Transaction) error
GetBalance(ctx context.Context, addr common.Address) (
big.Int, error)
}

type mockBlockchainClient struct {
sendErr error
balance big.Int
balanceErr error
}

func (m mockBlockchainClient) SendTransaction(ctx context.Context, tx types.Transaction) error {
return m.sendErr
}

No mocking frameworks needed — Go interfaces make manual mocks trivial.

Integration Tests with testcontainers:

func TestSettlementIntegration(t testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
// Spin up a real Postgres in Docker
pgContainer, _ := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{
Image: "postgres:15",
Env: map[string]string{"POSTGRES_PASSWORD": "test"},
},
Started: true,
})
defer pgContainer.Terminate(ctx)
// Run migrations, test against real DB
}
Use -short flag to skip integration tests in CI fast path. Full integration suite runs on merge.

Key Points

  • Table-driven tests: struct slice + t.Run subtests
  • Interface-based mocks — no frameworks needed
  • testcontainers for real DB integration tests
  • Use -short flag to skip slow tests in fast CI
  • Test the behavior, not the implementation details

Navigate