소스 정보
- 저장소
- xu-xiang/everything-claude-code-zh
- 최근 소스 활동
- 2026년 3월 5일 01:40
- 감지된 SKILL.md 언어
- 중국어
- 스타
- 1,867
- 포크
- 306
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/xu-xiang/everything-claude-code-zh --skill golang-testing명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | golang-testing |
| description | Go 测试模式,包括表格驱动测试、子测试、基准测试、模糊测试和测试覆盖率。遵循具有惯用 Go 实践的 TDD 方法论。 |
| origin | ECC |
遵循测试驱动开发(TDD)方法论,编写可靠且易于维护的 Go 测试的全面模式。
RED → 先编写一个失败的测试
GREEN → 编写最少的代码使测试通过
REFACTOR → 在保持测试通过的同时改进代码
REPEAT → 继续处理下一个需求
// 步骤 1:定义接口/签名
// calculator.go
package calculator
func Add(a, b int) int {
panic("not implemented") // 占位符
}
// 步骤 2:编写失败的测试 (RED)
// calculator_test.go
package calculator
import "testing"
func TestAdd(t *testing.T) {
got := Add(2, 3)
want := 5
if got != want {
t.Errorf("Add(2, 3) = %d; want %d", got, want)
}
}
// 步骤 3:运行测试 - 验证失败 (FAIL)
// $ go test
// --- FAIL: TestAdd (0.00s)
// panic: not implemented
// 步骤 4:实现最少代码 (GREEN)
func Add(a, b int) int {
return a + b
}
// 步骤 5:运行测试 - 验证通过 (PASS)
// $ go test
// PASS
// 步骤 6:如果需要则进行重构,并验证测试仍然通过
Go 测试的标准模式。能够以最少的代码实现全面的覆盖。
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 5},
{"negative numbers", -1, -2, -3},
{"zero values", 0, 0, 0},
{"mixed signs", -1, 1, 0},
{"large numbers", 1000000, 2000000, 3000000},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Add(tt.a, tt.b)
if got != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d",
tt.a, tt.b, got, tt.expected)
}
})
}
}
func TestParseConfig(t *testing.T) {
tests := []struct {
name string
input string
want *Config
wantErr bool
}{
{
name: "valid config",
input: `{"host": "localhost", "port": 8080}`,
want: &Config{Host: "localhost", Port: 8080},
},
{
name: "invalid JSON",
input: `{invalid}`,
wantErr: true,
},
{
name: "empty input",
input: "",
wantErr: true,
},
{
name: "minimal config",
input: `{}`,
want: &Config{}, // 零值配置
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseConfig(tt.input)
if tt.wantErr {
if err == nil {
t.Error("expected error, got nil")
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("got %+v; want %+v", got, tt.want)
}
})
}
}
func TestUser(t *testing.T) {
// 所有子测试共享的设置
db := setupTestDB(t)
t.Run("Create", func(t *testing.T) {
user := &User{Name: "Alice"}
err := db.CreateUser(user)
if err != nil {
t.Fatalf("CreateUser failed: %v", err)
}
if user.ID == "" {
t.Error("expected user ID to be set")
}
})
t.Run("Get", func(t *testing.T) {
user, err := db.GetUser("alice-id")
if err != nil {
t.Fatalf("GetUser failed: %v", err)
}
if user.Name != "Alice" {
t.Errorf("got name %q; want %q", user.Name, "Alice")
}
})
t.Run("Update", func(t *testing.T) {
// ...
})
t.Run("Delete", func(t *testing.T) {
// ...
})
}
func TestParallel(t *testing.T) {
tests := []struct {
name string
input string
}{
{"case1", "input1"},
{"case2", "input2"},
{"case3", "input3"},
}
for _, tt := range tests {
tt := tt // 捕获循环变量
t.Run(tt.name, func(t *testing.T) {
t.Parallel() // 并行运行子测试
result := Process(tt.input)
// 断言...
_ = result
})
}
}
func setupTestDB(t *testing.T) *sql.DB {
t.Helper() // 将此函数标记为测试助手函数
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
// 测试结束时清理
t.Cleanup(func() {
db.Close()
})
// 运行迁移
if _, err := db.Exec(schema); err != nil {
t.Fatalf("failed to create schema: %v", err)
}
return db
}
func assertNoError(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func assertEqual[T comparable](t *testing.T, got, want T) {
t.Helper()
if got != want {
t.Errorf("got %v; want %v", got, want)
}
}
func TestFileProcessing(t *testing.T) {
// 创建临时目录 - 自动清理
tmpDir := t.TempDir()
// 创建测试文件
testFile := filepath.Join(tmpDir, "test.txt")
err := os.WriteFile(testFile, []byte("test content"), 0644)
if err != nil {
t.Fatalf("failed to create test file: %v", err)
}
// 运行测试
result, err := ProcessFile(testFile)
if err != nil {
t.Fatalf("ProcessFile failed: %v", err)
}
// 断言...
_ = result
}
针对存储在 testdata/ 中的预期输出文件进行测试。
var update = flag.Bool("update", false, "update golden files")
func TestRender(t *testing.T) {
tests := []struct {
name string
input Template
}{
{"simple", Template{Name: "test"}},
{"complex", Template{Name: "test", Items: []string{"a", "b"}}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Render(tt.input)
golden := filepath.Join("testdata", tt.name+".golden")
if *update {
// 更新 golden file: go test -update
err := os.WriteFile(golden, got, 0644)
if err != nil {
t.Fatalf("failed to update golden file: %v", err)
}
}
want, err := os.ReadFile(golden)
if err != nil {
t.Fatalf("failed to read golden file: %v", err)
}
if !bytes.Equal(got, want) {
t.Errorf("output mismatch:\ngot:\n%s\nwant:\n%s", got, want)
}
})
}
}
// 为依赖定义接口
type UserRepository interface {
GetUser(id string) (*User, error)
SaveUser(user *User) error
}
// 生产环境实现
type PostgresUserRepository struct {
db *sql.DB
}
func (r *PostgresUserRepository) GetUser(id string) (*User, error) {
// 真实的数据库查询
}
// 用于测试的 Mock 实现
type MockUserRepository struct {
GetUserFunc func(id string) (*User, error)
SaveUserFunc func(user *User) error
}
func (m *MockUserRepository) GetUser(id string) (*User, error) {
return m.GetUserFunc(id)
}
func (m *MockUserRepository) SaveUser(user *User) error {
return m.SaveUserFunc(user)
}
// 使用 mock 进行测试
func TestUserService(t *testing.T) {
mock := &MockUserRepository{
GetUserFunc: func(id string) (*User, error) {
if id == "123" {
return &User{ID: "123", Name: },
}
, ErrNotFound
},
}
service := NewUserService(mock)
user, err := service.GetUserProfile()
err != {
t.Fatalf(, err)
}
user.Name != {
t.Errorf(, user.Name, )
}
}
func BenchmarkProcess(b *testing.B) {
data := generateTestData(1000)
b.ResetTimer() // 不要计算准备时间
for i := 0; i < b.N; i++ {
Process(data)
}
}
// 运行:go test -bench=BenchmarkProcess -benchmem
// 输出:BenchmarkProcess-8 10000 105234 ns/op 4096 B/op 10 allocs/op
func BenchmarkSort(b *testing.B) {
sizes := []int{100, 1000, 10000, 100000}
for _, size := range sizes {
b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
data := generateRandomSlice(size)
b.ResetTimer()
for i := 0; i < b.N; i++ {
// 创建副本以避免对已排序的数据进行排序
tmp := make([]int, len(data))
copy(tmp, data)
sort.Ints(tmp)
}
})
}
}
func BenchmarkStringConcat(b *testing.B) {
parts := []string{"hello", "world", "foo", "bar", "baz"}
b.Run("plus", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var s string
for _, p := range parts {
s += p
}
_ = s
}
})
b.Run("builder", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var sb strings.Builder
for _, p := range parts {
sb.WriteString(p)
}
_ = sb.String()
}
})
b.Run("join", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = strings.Join(parts, "")
}
})
}
func FuzzParseJSON(f *testing.F) {
// 添加种子语料库
f.Add(`{"name": "test"}`)
f.Add(`{"count": 123}`)
f.Add(`[]`)
f.Add(`""`)
f.Fuzz(func(t *testing.T, input string) {
var result map[string]interface{}
err := json.Unmarshal([]byte(input), &result)
if err != nil {
// 对于随机输入,预期的结果是无效的 JSON
return
}
// 如果解析成功,重新编码应该也有效
_, err = json.Marshal(result)
if err != nil {
t.Errorf("Marshal failed after successful Unmarshal: %v", err)
}
})
}
// 运行:go test -fuzz=FuzzParseJSON -fuzztime=30s
func FuzzCompare(f *testing.F) {
f.Add("hello", "world")
f.Add("", "")
f.Add("abc", "abc")
f.Fuzz(func(t *testing.T, a, b string) {
result := Compare(a, b)
// 属性:Compare(a, a) 应该始终等于 0
if a == b && result != 0 {
t.Errorf("Compare(%q, %q) = %d; want 0", a, b, result)
}
// 属性:Compare(a, b) 和 Compare(b, a) 应该符号相反
reverse := Compare(b, a)
if (result > 0 && reverse >= 0) || (result < 0 && reverse <= 0) {
if result != 0 || reverse != 0 {
t.Errorf("Compare(%q, %q) = %d, Compare(%q, %q) = %d; inconsistent",
a, b, result, b, a, reverse)
}
}
})
}
# 基础覆盖率
go test -cover ./...
# 生成覆盖率配置文件
go test -coverprofile=coverage.out ./...
# 在浏览器中查看覆盖率
go tool cover -html=coverage.out
# 按函数查看覆盖率
go tool cover -func=coverage.out
# 带有竞态检测的覆盖率
go test -race -coverprofile=coverage.out ./...
| 代码类型 | 目标 |
|---|---|
| 关键业务逻辑 | 100% |
| 公共 API | 90%+ |
| 通用代码 | 80%+ |
| 生成的代码 | 排除 |
//go:generate mockgen -source=interface.go -destination=mock_interface.go
// 在覆盖率配置文件中,使用构建标签排除:
// go test -cover -tags=!generate ./...
func TestHealthHandler(t *testing.T) {
// 创建请求
req := httptest.NewRequest(http.MethodGet, "/health", nil)
w := httptest.NewRecorder()
// 调用 handler
HealthHandler(w, req)
// 检查响应
resp := w.Result()
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("got status %d; want %d", resp.StatusCode, http.StatusOK)
}
body, _ := io.ReadAll(resp.Body)
if string(body) != "OK" {
t.Errorf("got body %q; want %q", body, "OK")
}
}
func TestAPIHandler(t *testing.T) {
tests := []struct {
name string
method string
path string
body string
wantStatus int
wantBody string
}{
{
name: "get user",
method: http.MethodGet,
path: "/users/123",
wantStatus: http.StatusOK,
wantBody: `{"id":"123","name":"Alice"}`,
},
{
name: "not found",
method: http.MethodGet,
path: "/users/999",
wantStatus: http.StatusNotFound,
},
{
name: "create user",
method: http.MethodPost,
path: "/users",
body: `{"name":"Bob"}`,
wantStatus: http.StatusCreated,
},
}
handler := NewAPIHandler()
for _, tt := range tests {
t.Run(tt.name, {
body io.Reader
tt.body != {
body = strings.NewReader(tt.body)
}
req := httptest.NewRequest(tt.method, tt.path, body)
req.Header.Set(, )
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
w.Code != tt.wantStatus {
t.Errorf(, w.Code, tt.wantStatus)
}
tt.wantBody != && w.Body.String() != tt.wantBody {
t.Errorf(, w.Body.String(), tt.wantBody)
}
})
}
}
# 运行所有测试
go test ./...
# 运行测试并输出详细信息
go test -v ./...
# 运行特定测试
go test -run TestAdd ./...
# 运行匹配模式的测试
go test -run "TestUser/Create" ./...
# 运行带有竞态检测器的测试
go test -race ./...
# 运行带有覆盖率检查的测试
go test -cover -coverprofile=coverage.out ./...
# 仅运行短测试
go test -short ./...
# 运行带有超时的测试
go test -timeout 30s ./...
# 运行基准测试
go test -bench=. -benchmem ./...
# 运行模糊测试
go test -fuzz=FuzzParse -fuzztime=30s ./...
# 统计测试运行次数(用于检测不稳定的测试)
go test -count=10 ./...
建议 (DO):
t.Helper()t.Parallel()t.Cleanup() 清理资源避免 (DON'T):
time.Sleep()(应使用通道或条件)# GitHub Actions 示例
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Run tests
run: go test -race -coverprofile=coverage.out ./...
- name: Check coverage
run: |
go tool cover -func=coverage.out | grep total | awk '{print $3}' | \
awk -F'%' '{if ($1 < 80) exit 1}'
记住:测试即文档。它们展示了代码应该如何被使用。请清晰地编写测试并保持更新。