mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-02-11 08:53:08 +08:00
Add `text` language identifier to unlabeled fenced code blocks to satisfy markdownlint MD040 rule across Go documentation files.
269 lines
5.5 KiB
Markdown
269 lines
5.5 KiB
Markdown
---
|
|
description: Enforce TDD workflow for Go. Write table-driven tests first, then implement. Verify 80%+ coverage with go test -cover.
|
|
---
|
|
|
|
# Go TDD Command
|
|
|
|
This command enforces test-driven development methodology for Go code using idiomatic Go testing patterns.
|
|
|
|
## What This Command Does
|
|
|
|
1. **Define Types/Interfaces**: Scaffold function signatures first
|
|
2. **Write Table-Driven Tests**: Create comprehensive test cases (RED)
|
|
3. **Run Tests**: Verify tests fail for the right reason
|
|
4. **Implement Code**: Write minimal code to pass (GREEN)
|
|
5. **Refactor**: Improve while keeping tests green
|
|
6. **Check Coverage**: Ensure 80%+ coverage
|
|
|
|
## When to Use
|
|
|
|
Use `/go-test` when:
|
|
- Implementing new Go functions
|
|
- Adding test coverage to existing code
|
|
- Fixing bugs (write failing test first)
|
|
- Building critical business logic
|
|
- Learning TDD workflow in Go
|
|
|
|
## TDD Cycle
|
|
|
|
```
|
|
RED → Write failing table-driven test
|
|
GREEN → Implement minimal code to pass
|
|
REFACTOR → Improve code, tests stay green
|
|
REPEAT → Next test case
|
|
```
|
|
|
|
## Example Session
|
|
|
|
```text
|
|
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!
|
|
```
|
|
|
|
## Test Patterns
|
|
|
|
### Table-Driven Tests
|
|
```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
|
|
})
|
|
}
|
|
```
|
|
|
|
### Parallel Tests
|
|
```go
|
|
for _, tt := range tests {
|
|
tt := tt // Capture
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
// test body
|
|
})
|
|
}
|
|
```
|
|
|
|
### Test Helpers
|
|
```go
|
|
func setupTestDB(t *testing.T) *sql.DB {
|
|
t.Helper()
|
|
db := createDB()
|
|
t.Cleanup(func() { db.Close() })
|
|
return db
|
|
}
|
|
```
|
|
|
|
## Coverage Commands
|
|
|
|
```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 ./...
|
|
```
|
|
|
|
## Coverage Targets
|
|
|
|
| Code Type | Target |
|
|
|-----------|--------|
|
|
| Critical business logic | 100% |
|
|
| Public APIs | 90%+ |
|
|
| General code | 80%+ |
|
|
| Generated code | Exclude |
|
|
|
|
## TDD Best Practices
|
|
|
|
**DO:**
|
|
- Write test FIRST, before any implementation
|
|
- Run tests after each change
|
|
- Use table-driven tests for comprehensive coverage
|
|
- Test behavior, not implementation details
|
|
- Include edge cases (empty, nil, max values)
|
|
|
|
**DON'T:**
|
|
- Write implementation before tests
|
|
- Skip the RED phase
|
|
- Test private functions directly
|
|
- Use `time.Sleep` in tests
|
|
- Ignore flaky tests
|
|
|
|
## Related Commands
|
|
|
|
- `/go-build` - Fix build errors
|
|
- `/go-review` - Review code after implementation
|
|
- `/verify` - Run full verification loop
|
|
|
|
## Related
|
|
|
|
- Skill: `skills/golang-testing/`
|
|
- Skill: `skills/tdd-workflow/`
|