plugins/languages/golang/skills/testing/SKILL.md
Go 测试规范——表驱动测试(t.Run 子测试)、testing/synctest 确定性并发测试(Go 1.25 GA)、模糊测试(go test -fuzz)、基准测试(B.Loop, Go 1.24+)、testify 断言、mock 全局 state、覆盖率 ≥90%。写单元测试/集成测试/benchmark、调查 flaky 测试、设计 mock 策略时触发。
npx skillsauth add lazygophers/ccplugin golang-testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
_test.go,函数命名 TestXxx/BenchmarkXxx/FuzzXxx。t.Run 子测试。time.Sleep 等待异步,并发测试用 testing/synctest(Go 1.25 GA)。t.Parallel() 默认开。func TestUserLogin(t *testing.T) {
tests := []struct {
name string
username string
password string
wantErr bool
}{
{"valid", "user", "pass123", false},
{"wrong password", "user", "wrong", true},
{"empty user", "", "pass123", true},
{"empty pass", "user", "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
user, err := UserLogin(tt.username, tt.password)
if (err != nil) != tt.wantErr {
t.Errorf("UserLogin() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && user == nil {
t.Error("UserLogin() returned nil user")
}
})
}
}
注:Go 1.22+ 循环变量每轮新建,无需 tt := tt。
杀手锏:测试涉及 time.After/context.WithTimeout/goroutine 协作时,告别 time.Sleep 和 flaky 测试。
import "testing/synctest"
func TestContextDeadline(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
synctest.Wait() // 等所有 goroutine durably blocked
select {
case <-ctx.Done():
// ctx 已超时(假时钟瞬间推进)
default:
t.Fatal("ctx should be done")
}
})
}
time 用假时钟,零等待。synctest.Wait() 阻塞到 bubble 内所有 goroutine durably blocked。func FuzzParseJSON(f *testing.F) {
f.Add(`{"name":"test"}`)
f.Add(`{}`)
f.Add(`""`)
f.Fuzz(func(t *testing.T, input string) {
result, err := ParseJSON(input)
if err != nil { return }
if result == nil {
t.Error("ParseJSON returned nil without error")
}
})
}
go test -fuzz=FuzzParseJSON -fuzztime=30s ./parser/
解析器、编解码器、URL 处理类强制写 fuzz。
func BenchmarkProcessData(b *testing.B) {
data := generateTestData(1000)
b.ReportAllocs()
for b.Loop() { // Go 1.24+,自动 ResetTimer + 避免编译器消除
ProcessData(data)
}
}
对比:
go test -bench=. -benchmem -count=5 > old.txt
# 修改代码后
go test -bench=. -benchmem -count=5 > new.txt
benchstat old.txt new.txt
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCreate(t *testing.T) {
user, err := CreateUser("[email protected]")
require.NoError(t, err) // 失败即停
require.NotNil(t, user)
assert.Equal(t, "[email protected]", user.Email)
}
require 用于必要前提,失败即停;assert 用于次要断言,可累积。
func TestUserLoginWithMock(t *testing.T) {
orig := state.User
defer func() { state.User = orig }()
state.User = &MockUserModel{
users: map[int64]*User{1: {Id: 1, Email: "[email protected]"}},
}
user, err := UserLogin("[email protected]", "pwd")
require.NoError(t, err)
assert.Equal(t, "[email protected]", user.Email)
}
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out | grep total
go tool cover -html=coverage.out
go test -v -race -cover ./...
CI 中用 vladopajic/go-test-coverage 配置 .testcoverage.yml 设阈值。
| AI 借口 | 实际应验证 | | --- | --- | | "80% 覆盖率够了" | ≥90%,关键路径 100%? | | "fuzz 太慢" | 解析器/编解码器有 fuzz? | | "time.Sleep 等等" | 用 testing/synctest? | | "mock 一切" | 仅 mock 外部依赖? | | "Test1/Test2" | 子测试 name 描述性? | | "跳过错误路径" | 错误路径有用例? |
_test.go、函数 Test/Benchmark/Fuzz 前缀t.Runt.Parallel() 开启testing/synctest-racetesting/synctest 官博 — https://go.dev/blog/synctestdevelopment
Go 数据库规范——GORM Model 命名 ModelXxx、表名单数、枚举 uint8 + 常量、索引 idx_ 前缀 + deleted_at leading column、禁 time.Time 统一 int64 unix、禁指针/nullable 字段、TEXT/BLOB/JSON 禁 default、AutoMigrate 禁改主键。设计 DB model、写 GORM tag、建索引、做 migration 审查时触发。
development
Go HTTP API 规范——响应始终 200 + body code 字段、路由 /api/* 全 POST 单段 <Action><Model>、中间件逐路由注册禁 Group(prefix,mw...)、handler 仅返回 (rsp,error)、认证走 header。设计 HTTP API、写路由/handler/中间件时触发。
development
Go 项目结构规范——三层架构(API → Impl → State)、全局状态模式、internal/ 私有包、cmd/ 仅 main.go、go.work 多模块、禁止 Repository 接口和 DI 容器、struct 公共字段开头全 omitempty、handler var rsp 顶声明、禁 legacy migration。设计项目骨架、新建目录、组织包、做架构评审时触发。
development
Go 命名规范——Id/Uid 字段(非 ID)、IsActive/HasMFA 布尔前缀、CreatedAt 时间字段、接收者统一用 p、包名全小写无下划线、泛型类型参数描述性命名、集合字段 xxx_list 禁 xxxs 复数、Enum 0 值 XxxNil 禁 Unknown、禁 Status 统一 State、Set/Update 语义区分。定义结构体字段、函数、变量、包、接收者名、泛型、枚举时触发。