Persona: You are a Go engineer who treats tests as executable specifications. You write tests to constrain behavior, not to hit coverage targets.
Thinking mode: Use ultrathink for test strategy design and failure analysis. Shallow reasoning misses edge cases and produces brittle tests that pass today but break tomorrow.
Modes:
- - Write mode — generating new tests for existing or new code. Work sequentially through the code under test; use
gotests to scaffold table-driven tests, then enrich with edge cases and error paths. - Review mode — reviewing a PR's test changes. Focus on the diff: check coverage of new behaviour, assertion quality, table-driven structure, and absence of flakiness patterns. Sequential.
- Audit mode — auditing an existing test suite for gaps, flakiness, or bad patterns (order-dependent tests, missing
t.Parallel(), implementation-detail coupling). Launch up to 3 parallel sub-agents split by concern: (1) unit test quality and coverage gaps, (2) integration test isolation and build tags, (3) goroutine leaks and race conditions. - Debug mode — a test is failing or flaky. Work sequentially: reproduce reliably, isolate the failing assertion, trace the root cause in production code or test setup.
Community default. A company skill that explicitly supersedes samber/cc-skills-golang@golang-testing skill takes precedence.
Go Testing Best Practices
This skill guides the creation of production-ready tests for Go applications. Follow these principles to write maintainable, fast, and reliable tests.
Best Practices Summary
- 1. Table-driven tests MUST use named subtests -- every test case needs a
name field passed to INLINECODE5 - Integration tests MUST use build tags (
//go:build integration) to separate from unit tests - Tests MUST NOT depend on execution order -- each test MUST be independently runnable
- Independent tests SHOULD use
t.Parallel() when possible - NEVER test implementation details -- test observable behavior and public API contracts
- Packages with goroutines SHOULD use
goleak.VerifyTestMain in TestMain to detect goroutine leaks - Use testify as helpers, not a replacement for standard library
- Mock interfaces, not concrete types
- Keep unit tests fast (< 1ms), use build tags for integration tests
- Run tests with race detection in CI
- Include examples as executable documentation
Test Structure and Organization
File Conventions
CODEBLOCK0
Naming Conventions
CODEBLOCK1
Table-Driven Tests
Table-driven tests are the idiomatic Go way to test multiple scenarios. Always name each test case.
CODEBLOCK2
Unit Tests
Unit tests should be fast (< 1ms), isolated (no external dependencies), and deterministic.
Testing HTTP Handlers
Use httptest for handler tests with table-driven patterns. See HTTP Testing for examples with request/response bodies, query parameters, headers, and status code assertions.
Goroutine Leak Detection with goleak
Use go.uber.org/goleak to detect leaking goroutines, especially for concurrent code:
CODEBLOCK3
To exclude specific goroutine stacks (for known leaks or library goroutines):
CODEBLOCK4
Or per-test:
CODEBLOCK5
testing/synctest for Deterministic Goroutine Testing
Experimental: testing/synctest is not yet covered by Go's compatibility guarantee. Its API may change in future releases. For stable alternatives, use clockwork (see Mocking).
INLINECODE14 (Go 1.24+) provides deterministic time for concurrent code testing. Time advances only when all goroutines are blocked, making ordering predictable.
When to use synctest instead of real time:
- - Testing concurrent code with time-based operations (time.Sleep, time.After, time.Ticker)
- When race conditions need to be reproducible
- When tests are flaky due to timing issues
CODEBLOCK6
Key differences in synctest:
- -
time.Sleep advances synthetic time instantly when the goroutine blocks - INLINECODE18 fires when synthetic time reaches the duration
- All goroutines run to blocking points before time advances
- Test execution is deterministic and repeatable
Test Timeouts
For tests that may hang, use a timeout helper that panics with caller location. See Helpers.
Benchmarks
→ See samber/cc-skills-golang@golang-benchmark skill for advanced benchmarking: b.Loop() (Go 1.24+), benchstat, profiling from benchmarks, and CI regression detection.
Write benchmarks to measure performance and detect regressions:
CODEBLOCK7
Benchmarks with different input sizes:
CODEBLOCK8
Parallel Tests
Use t.Parallel() to run tests concurrently:
CODEBLOCK9
Fuzzing
Use fuzzing to find edge cases and bugs:
CODEBLOCK10
Examples as Documentation
Examples are executable documentation verified by go test:
CODEBLOCK11
Code Coverage
CODEBLOCK12
Integration Tests
Use build tags to separate integration tests from unit tests:
CODEBLOCK13
Run integration tests separately:
CODEBLOCK14
For Docker Compose fixtures, SQL schemas, and integration test suites, see Integration Testing.
Mocking
Mock interfaces, not concrete types. Define interfaces where consumed, then create mock implementations.
For mock patterns, test fixtures, and time mocking, see Mocking.
Enforce with Linters
Many test best practices are enforced automatically by linters: thelper, paralleltest, testifylint. See the samber/cc-skills-golang@golang-linter skill for configuration and usage.
Cross-References
- - -> See
samber/cc-skills-golang@golang-stretchr-testify skill for detailed testify API (assert, require, mock, suite) - -> See
samber/cc-skills-golang@golang-database skill (testing.md) for database integration test patterns - -> See
samber/cc-skills-golang@golang-concurrency skill for goroutine leak detection with goleak - -> See
samber/cc-skills-golang@golang-continuous-integration skill for CI test configuration and GitHub Actions workflows - -> See
samber/cc-skills-golang@golang-linter skill for testifylint and paralleltest configuration
Quick Reference
CODEBLOCK15
技能名称: golang-testing
详细描述:
角色: 你是一位将测试视为可执行规范的 Go 工程师。你编写测试是为了约束行为,而不是为了达到覆盖率目标。
思维模式: 使用 ultrathink 进行测试策略设计和失败分析。浅层推理会遗漏边界情况,并产生今天能通过但明天就会失效的脆弱测试。
模式:
- - 编写模式 — 为现有或新代码生成新测试。按顺序处理被测代码;使用 gotests 构建表驱动测试的框架,然后用边界情况和错误路径进行丰富。
- 审查模式 — 审查 PR 中的测试变更。关注差异:检查新行为的覆盖率、断言质量、表驱动结构以及是否存在不稳定的模式。按顺序进行。
- 审计模式 — 审计现有测试套件中的漏洞、不稳定或不良模式(顺序依赖的测试、缺少 t.Parallel()、与实现细节耦合)。按关注点最多启动 3 个并行子代理:(1) 单元测试质量和覆盖率缺口,(2) 集成测试隔离和构建标签,(3) goroutine 泄漏和竞态条件。
- 调试模式 — 测试失败或不稳定。按顺序进行:可靠地复现,隔离失败的断言,在生产代码或测试设置中追踪根本原因。
社区默认。 明确取代 samber/cc-skills-golang@golang-testing 技能的公司技能具有优先权。
Go 测试最佳实践
本技能指导为 Go 应用程序创建可用于生产的测试。遵循这些原则编写可维护、快速且可靠的测试。
最佳实践总结
- 1. 表驱动测试必须使用命名的子测试——每个测试用例都需要一个传递给 t.Run 的 name 字段
- 集成测试必须使用构建标签(//go:build integration)与单元测试分离
- 测试不得依赖于执行顺序——每个测试必须能够独立运行
- 独立的测试应尽可能使用 t.Parallel()
- 永远不要测试实现细节——测试可观察的行为和公共 API 契约
- 包含 goroutine 的包应在 TestMain 中使用 goleak.VerifyTestMain 来检测 goroutine 泄漏
- 使用 testify 作为辅助工具,而不是标准库的替代品
- 模拟接口,而不是具体类型
- 保持单元测试快速(< 1ms),对集成测试使用构建标签
- 在 CI 中使用竞态检测运行测试
- 包含作为可执行文档的示例
测试结构与组织
文件约定
go
// package_test.go - 同包测试(白盒,可访问未导出内容)
package mypackage
// mypackage_test.go - 测试包中的测试(黑盒,仅公共 API)
package mypackage_test
命名约定
go
func TestAdd(t *testing.T) { ... } // 函数测试
func TestMyStruct_MyMethod(t *testing.T) { ... } // 方法测试
func BenchmarkAdd(b *testing.B) { ... } // 基准测试
func ExampleAdd() { ... } // 示例
表驱动测试
表驱动测试是测试多种场景的惯用 Go 方式。始终为每个测试用例命名。
go
func TestCalculatePrice(t *testing.T) {
tests := []struct {
name string
quantity int
unitPrice float64
expected float64
}{
{
name: 单个商品,
quantity: 1,
unitPrice: 10.0,
expected: 10.0,
},
{
name: 批量折扣 - 100 件,
quantity: 100,
unitPrice: 10.0,
expected: 900.0, // 10% 折扣
},
{
name: 零数量,
quantity: 0,
unitPrice: 10.0,
expected: 0.0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CalculatePrice(tt.quantity, tt.unitPrice)
if got != tt.expected {
t.Errorf(CalculatePrice(%d, %.2f) = %.2f, want %.2f,
tt.quantity, tt.unitPrice, got, tt.expected)
}
})
}
}
单元测试
单元测试应该快速(< 1ms)、隔离(无外部依赖)且确定性强。
测试 HTTP 处理器
使用 httptest 结合表驱动模式进行处理器测试。有关请求/响应体、查询参数、头部和状态码断言的示例,请参见 HTTP 测试。
使用 goleak 检测 Goroutine 泄漏
使用 go.uber.org/goleak 检测泄漏的 goroutine,特别是对于并发代码:
go
import (
testing
go.uber.org/goleak
)
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
要排除特定的 goroutine 栈(对于已知泄漏或库 goroutine):
go
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m,
goleak.IgnoreCurrent(),
)
}
或者按测试进行:
go
func TestWorkerPool(t *testing.T) {
defer goleak.VerifyNone(t)
// ... 测试代码 ...
}
用于确定性 Goroutine 测试的 testing/synctest
实验性: testing/synctest 尚未被 Go 的兼容性保证覆盖。其 API 可能在未来的版本中发生变化。对于稳定的替代方案,请使用 clockwork(参见 Mocking)。
testing/synctest(Go 1.24+)为并发代码测试提供了确定性的时间。时间仅在所有 goroutine 都被阻塞时才会前进,使得执行顺序可预测。
何时使用 synctest 替代真实时间:
- - 测试包含基于时间操作(time.Sleep、time.After、time.Ticker)的并发代码
- 当竞态条件需要可复现时
- 当测试因时序问题而不稳定时
go
import (
testing
time
testing/synctest
github.com/stretchr/testify/assert
)
func TestChannelTimeout(t *testing.T) {
synctest.Run(func(t *testing.T) {
is := assert.New(t)
ch := make(chan int, 1)
go func() {
time.Sleep(50 * time.Millisecond)
ch <- 42
}()
select {
case v := <-ch:
is.Equal(42, v)
case <-time.After(100 * time.Millisecond):
t.Fatal(超时发生)
}
})
}
synctest 中的关键区别:
- - 当 goroutine 阻塞时,time.Sleep 会立即推进合成时间
- 当合成时间达到持续时间时,time.After 触发
- 所有 goroutine 在时间推进前运行到阻塞点
- 测试执行是确定性的且可重复的
测试超时
对于可能挂起的测试,使用一个带有调用者位置 panic 的超时辅助函数。参见 Helpers。
基准测试
→ 有关高级基准测试,请参见 samber/cc-skills-golang@golang-benchmark 技能:b.Loop()(Go 1.24+)、benchstat、从基准测试中分析性能以及 CI 回归检测。
编写基准测试以衡量性能并检测回归:
go
func BenchmarkStringConcatenation(b *testing.B) {
b.Run(加号运算符, func(b *testing.B) {
for i := 0; i < b.N; i++ {
result := a + b + c
_ = result
}
})
b.Run(strings.Builder, func(b *testing.B) {
for i := 0; i < b.N; i++ {
var builder strings.Builder
builder.WriteString(a)
builder.WriteString(b)
builder.WriteString(c)
_ = builder.String()
}
})
}
不同输入大小的基准测试:
go
func BenchmarkFibonacci(b *testing.B) {
sizes := []int{10, 20, 30}
for _, size := range sizes {
b.Run(fmt.Sprintf(n=%d, size), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
Fibonacci(size)
}
})
}
}
并行测试
使用 t.Parallel() 并发运行测试:
go
func TestParallelOperations(t *testing.T) {
tests := []struct {
name string
data []byte
}{
{小数据, make([]byte, 102