mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-01 22:53:27 +08:00
275 lines
5.5 KiB
Markdown
275 lines
5.5 KiB
Markdown
---
|
||
description: 为Go强制执行TDD工作流程。首先编写表驱动测试,然后实现。使用go test -cover验证80%以上的覆盖率。
|
||
---
|
||
|
||
# Go TDD 命令
|
||
|
||
此命令使用惯用的 Go 测试模式,为 Go 代码强制执行测试驱动开发方法。
|
||
|
||
## 此命令的作用
|
||
|
||
1. **定义类型/接口**:首先搭建函数签名
|
||
2. **编写表驱动测试**:创建全面的测试用例(RED 阶段)
|
||
3. **运行测试**:验证测试因正确原因而失败
|
||
4. **实现代码**:编写最少的代码以通过测试(GREEN 阶段)
|
||
5. **重构**:改进代码,同时保持测试通过
|
||
6. **检查覆盖率**:确保 80% 以上的覆盖率
|
||
|
||
## 何时使用
|
||
|
||
在以下情况下使用 `/go-test`:
|
||
|
||
* 实现新的 Go 函数时
|
||
* 为现有代码添加测试覆盖率时
|
||
* 修复 bug 时(先编写失败的测试)
|
||
* 构建关键业务逻辑时
|
||
* 学习 Go 中的 TDD 工作流程时
|
||
|
||
## TDD 循环
|
||
|
||
```
|
||
RED → Write failing table-driven test
|
||
GREEN → Implement minimal code to pass
|
||
REFACTOR → Improve code, tests stay green
|
||
REPEAT → Next test case
|
||
```
|
||
|
||
## 示例会话
|
||
|
||
````
|
||
User: /go-test I need a function to validate email addresses
|
||
|
||
Agent:
|
||
# TDD Session: Email Validator
|
||
|
||
## Step 1: Define Interface
|
||
|
||
```go
|
||
// validator/email.go
|
||
package validator
|
||
|
||
// ValidateEmail checks if the given string is a valid email address.
|
||
// Returns nil if valid, error describing the issue otherwise.
|
||
func ValidateEmail(email string) error {
|
||
panic("not implemented")
|
||
}
|
||
```
|
||
|
||
## Step 2: Write Table-Driven Tests (RED)
|
||
|
||
```go
|
||
// validator/email_test.go
|
||
package validator
|
||
|
||
import (
|
||
"testing"
|
||
)
|
||
|
||
func TestValidateEmail(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
email string
|
||
wantErr bool
|
||
}{
|
||
// Valid emails
|
||
{"simple email", "user@example.com", false},
|
||
{"with subdomain", "user@mail.example.com", false},
|
||
{"with plus", "user+tag@example.com", false},
|
||
{"with dots", "first.last@example.com", false},
|
||
|
||
// Invalid emails
|
||
{"empty string", "", true},
|
||
{"no at sign", "userexample.com", true},
|
||
{"no domain", "user@", true},
|
||
{"no local part", "@example.com", true},
|
||
{"double at", "user@@example.com", true},
|
||
{"spaces", "user @example.com", true},
|
||
{"no tld", "user@example", true},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
err := ValidateEmail(tt.email)
|
||
if tt.wantErr && err == nil {
|
||
t.Errorf("ValidateEmail(%q) = nil; want error", tt.email)
|
||
}
|
||
if !tt.wantErr && err != nil {
|
||
t.Errorf("ValidateEmail(%q) = %v; want nil", tt.email, err)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
## Step 3: Run Tests - Verify FAIL
|
||
|
||
```bash
|
||
$ go test ./validator/...
|
||
|
||
--- FAIL: TestValidateEmail (0.00s)
|
||
--- FAIL: TestValidateEmail/simple_email (0.00s)
|
||
panic: not implemented
|
||
|
||
FAIL
|
||
```
|
||
|
||
✓ Tests fail as expected (panic).
|
||
|
||
## Step 4: Implement Minimal Code (GREEN)
|
||
|
||
```go
|
||
// validator/email.go
|
||
package validator
|
||
|
||
import (
|
||
"errors"
|
||
"regexp"
|
||
)
|
||
|
||
var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
||
|
||
var (
|
||
ErrEmailEmpty = errors.New("email cannot be empty")
|
||
ErrEmailInvalid = errors.New("email format is invalid")
|
||
)
|
||
|
||
func ValidateEmail(email string) error {
|
||
if email == "" {
|
||
return ErrEmailEmpty
|
||
}
|
||
if !emailRegex.MatchString(email) {
|
||
return ErrEmailInvalid
|
||
}
|
||
return nil
|
||
}
|
||
```
|
||
|
||
## Step 5: Run Tests - Verify PASS
|
||
|
||
```bash
|
||
$ go test ./validator/...
|
||
|
||
PASS
|
||
ok project/validator 0.003s
|
||
```
|
||
|
||
✓ All tests passing!
|
||
|
||
## Step 6: Check Coverage
|
||
|
||
```bash
|
||
$ go test -cover ./validator/...
|
||
|
||
PASS
|
||
coverage: 100.0% of statements
|
||
ok project/validator 0.003s
|
||
```
|
||
|
||
✓ Coverage: 100%
|
||
|
||
## TDD Complete!
|
||
````
|
||
|
||
## 测试模式
|
||
|
||
### 表驱动测试
|
||
|
||
```go
|
||
tests := []struct {
|
||
name string
|
||
input InputType
|
||
want OutputType
|
||
wantErr bool
|
||
}{
|
||
{"case 1", input1, want1, false},
|
||
{"case 2", input2, want2, true},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
got, err := Function(tt.input)
|
||
// assertions
|
||
})
|
||
}
|
||
```
|
||
|
||
### 并行测试
|
||
|
||
```go
|
||
for _, tt := range tests {
|
||
tt := tt // Capture
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
t.Parallel()
|
||
// test body
|
||
})
|
||
}
|
||
```
|
||
|
||
### 测试辅助函数
|
||
|
||
```go
|
||
func setupTestDB(t *testing.T) *sql.DB {
|
||
t.Helper()
|
||
db := createDB()
|
||
t.Cleanup(func() { db.Close() })
|
||
return db
|
||
}
|
||
```
|
||
|
||
## 覆盖率命令
|
||
|
||
```bash
|
||
# Basic coverage
|
||
go test -cover ./...
|
||
|
||
# Coverage profile
|
||
go test -coverprofile=coverage.out ./...
|
||
|
||
# View in browser
|
||
go tool cover -html=coverage.out
|
||
|
||
# Coverage by function
|
||
go tool cover -func=coverage.out
|
||
|
||
# With race detection
|
||
go test -race -cover ./...
|
||
```
|
||
|
||
## 覆盖率目标
|
||
|
||
| 代码类型 | 目标 |
|
||
|-----------|--------|
|
||
| 关键业务逻辑 | 100% |
|
||
| 公共 API | 90%+ |
|
||
| 通用代码 | 80%+ |
|
||
| 生成的代码 | 排除 |
|
||
|
||
## TDD 最佳实践
|
||
|
||
**应该做:**
|
||
|
||
* 先编写测试,再编写任何实现
|
||
* 每次更改后运行测试
|
||
* 使用表驱动测试以获得全面的覆盖率
|
||
* 测试行为,而非实现细节
|
||
* 包含边界情况(空值、nil、最大值)
|
||
|
||
**不应该做:**
|
||
|
||
* 在编写测试之前编写实现
|
||
* 跳过 RED 阶段
|
||
* 直接测试私有函数
|
||
* 在测试中使用 `time.Sleep`
|
||
* 忽略不稳定的测试
|
||
|
||
## 相关命令
|
||
|
||
* `/go-build` - 修复构建错误
|
||
* `/go-review` - 在实现后审查代码
|
||
* `/verify` - 运行完整的验证循环
|
||
|
||
## 相关
|
||
|
||
* 技能:`skills/golang-testing/`
|
||
* 技能:`skills/tdd-workflow/`
|