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:
1. 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)
2. 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
3. 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
4. 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
5. 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")
6. 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
7. 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
8. 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)
9. 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
10. 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.
1---2name: writing-go-tests3description: Applies current Go testing best practices. Use when writing or modifying Go test files or advising on Go testing strategies.4---56# Go Testing Best Practices78This skill provides actionable testing guidelines. For detailed implementation patterns, code examples, rationale, and production system references, consult `go-testing-best-practices.md`.910## When Working with Go Tests1112**Always apply these current best practices:**1314### 1. Test Organisation15- Place test files alongside source code using `*_test.go` naming16- Use internal tests (same package) for unit testing unexported functions17- Use external tests (`package foo_test`) for integration testing and examples18- Split test files by functionality when they exceed 500-800 lines (e.g., `handler_auth_test.go`, `handler_validation_test.go`)1920### 2. Table-Driven Testing21- **Prefer map-based tables over slice-based** for automatic unique test names22- Use descriptive test case names that appear in failure output23- See detailed guide for complete pattern and examples2425### 3. Concurrent Testing26- **Use `testing/synctest` for deterministic concurrent testing** (Go 1.24+)27- This eliminates flaky time-based tests and runs in microseconds instead of seconds28- For traditional parallel tests, always call `t.Parallel()` first in test functions2930### 4. Assertions and Comparisons31- Use `cmp.Diff()` from `google/go-cmp` for complex comparisons32- Standard library is sufficient for simple tests33- Testify is the dominant third-party framework when richer assertions are needed3435### 5. Mocking and Test Doubles36- **Favour integration testing with real dependencies** over heavy mocking37- Use Testcontainers for database/service integration tests38- When mocking is necessary, prefer simple function-based test doubles over code generation39- Use interface-based design ("accept interfaces, return structs")4041### 6. Coverage Targets42- Aim for **70-80% coverage as a practical target**43- Focus on meaningful tests over percentage metrics44- Use `go test -cover` and `go tool cover -html` for analysis4546### 7. Test Fixtures47- Use `testdata` directory for test fixtures (automatically ignored by Go toolchain)48- Implement golden file testing for validating complex output49- Use functional builder patterns for complex test data5051### 8. Helpers and Cleanup52- **Always mark helper functions with `t.Helper()`** for accurate error reporting53- Use `t.Cleanup()` for resource cleanup (superior to defer in tests)5455### 9. Benchmarking (Go 1.24+)56- **Use `B.Loop()` method** as the preferred pattern (prevents compiler optimisations)57- Combine with `benchstat` for statistical analysis58- Use `-benchmem` for memory profiling5960### 10. Naming Conventions61- Test functions: `Test*`, `Benchmark*`, `Fuzz*`, `Example*` (capital letter after prefix)62- Use `got` and `want` for actual vs expected values63- Use descriptive test case names in table-driven tests6465## Integration vs Unit Testing6667- **Separate tests by environment variable** (preferred over build tags)68- See detailed guide for implementation pattern6970## Additional Reference Material7172**Load `go-testing-best-practices.md` when you need:**73- Complete code examples for table-driven tests, mocking patterns, golden files, helpers, or benchmarks74- Detailed explanation of testing/synctest concurrent testing patterns75- Rationale behind why specific patterns are preferred over alternatives76- Production system examples and statistics (Kubernetes, Docker, Uber, Netflix, ByteDance)77- Context on testing framework choices (Testify, GoMock, Testcontainers)78- Comprehensive coverage strategies and tooling details79- Integration testing patterns with containerisation8081**The detailed guide contains full context, examples with explanations, and production-proven patterns. This SKILL.md provides the actionable rules to apply.**8283## Key Principle8485**Focus on meaningful tests that validate behaviour rather than implementation.** Pragmatic excellence over theoretical perfection.