Go Testing Best Practices
This skill provides actionable testing guidelines. For detailed implementation patterns, code examples, rationale, and production system references, consult go-testing-best-practices.md .
When Working with Go Tests
Always apply these current best practices:
- Test Organisation
-
Place test files alongside source code using *_test.go naming
-
Use internal tests (same package) for unit testing unexported functions
-
Use external tests (package foo_test ) for integration testing and examples
-
Split test files by functionality when they exceed 500-800 lines (e.g., handler_auth_test.go , handler_validation_test.go )
- Table-Driven Testing
-
Prefer map-based tables over slice-based for automatic unique test names
-
Use descriptive test case names that appear in failure output
-
See detailed guide for complete pattern and examples
- Concurrent Testing
-
Use testing/synctest for deterministic concurrent testing (Go 1.24+)
-
This eliminates flaky time-based tests and runs in microseconds instead of seconds
-
For traditional parallel tests, always call t.Parallel() first in test functions
- Assertions and Comparisons
-
Use cmp.Diff() from google/go-cmp for complex comparisons
-
Standard library is sufficient for simple tests
-
Testify is the dominant third-party framework when richer assertions are needed
- Mocking and Test Doubles
-
Favour integration testing with real dependencies over heavy mocking
-
Use Testcontainers for database/service integration tests
-
When mocking is necessary, prefer simple function-based test doubles over code generation
-
Use interface-based design ("accept interfaces, return structs")
- Coverage Targets
-
Aim for 70-80% coverage as a practical target
-
Focus on meaningful tests over percentage metrics
-
Use go test -cover and go tool cover -html for analysis
- Test Fixtures
-
Use testdata directory for test fixtures (automatically ignored by Go toolchain)
-
Implement golden file testing for validating complex output
-
Use functional builder patterns for complex test data
- Helpers and Cleanup
-
Always mark helper functions with t.Helper() for accurate error reporting
-
Use t.Cleanup() for resource cleanup (superior to defer in tests)
- Benchmarking (Go 1.24+)
-
Use B.Loop() method as the preferred pattern (prevents compiler optimisations)
-
Combine with benchstat for statistical analysis
-
Use -benchmem for memory profiling
- Naming Conventions
-
Test functions: Test* , Benchmark* , Fuzz* , Example* (capital letter after prefix)
-
Use got and want for actual vs expected values
-
Use descriptive test case names in table-driven tests
Integration vs Unit Testing
-
Separate tests by environment variable (preferred over build tags)
-
See detailed guide for implementation pattern
Additional Reference Material
Load go-testing-best-practices.md when you need:
-
Complete code examples for table-driven tests, mocking patterns, golden files, helpers, or benchmarks
-
Detailed explanation of testing/synctest concurrent testing patterns
-
Rationale behind why specific patterns are preferred over alternatives
-
Production system examples and statistics (Kubernetes, Docker, Uber, Netflix, ByteDance)
-
Context on testing framework choices (Testify, GoMock, Testcontainers)
-
Comprehensive coverage strategies and tooling details
-
Integration testing patterns with containerisation
The detailed guide contains full context, examples with explanations, and production-proven patterns. This SKILL.md provides the actionable rules to apply.
Key Principle
Focus on meaningful tests that validate behaviour rather than implementation. Pragmatic excellence over theoretical perfection.