/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