mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-18 16:13:29 +08:00
Add Kiro skills (18 SKILL.md files) (#811)
Co-authored-by: Sungmin Hong <hsungmin@amazon.com>
This commit is contained in:
332
.kiro/skills/golang-testing/SKILL.md
Normal file
332
.kiro/skills/golang-testing/SKILL.md
Normal file
@@ -0,0 +1,332 @@
|
||||
---
|
||||
name: golang-testing
|
||||
description: >
|
||||
Go testing best practices including table-driven tests, test helpers,
|
||||
benchmarking, race detection, coverage analysis, and integration testing
|
||||
patterns. Use when writing or improving Go tests.
|
||||
metadata:
|
||||
origin: ECC
|
||||
globs: ["**/*.go", "**/go.mod", "**/go.sum"]
|
||||
---
|
||||
|
||||
# Go Testing
|
||||
|
||||
> This skill provides comprehensive Go testing patterns extending common testing principles with Go-specific idioms.
|
||||
|
||||
## Testing Framework
|
||||
|
||||
Use the standard `go test` with **table-driven tests** as the primary pattern.
|
||||
|
||||
### Table-Driven Tests
|
||||
|
||||
The idiomatic Go testing pattern:
|
||||
|
||||
```go
|
||||
func TestValidateEmail(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
email string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid email",
|
||||
email: "user@example.com",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing @",
|
||||
email: "userexample.com",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
email: "",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := ValidateEmail(tt.email)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ValidateEmail(%q) error = %v, wantErr %v",
|
||||
tt.email, err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Easy to add new test cases
|
||||
- Clear test case documentation
|
||||
- Parallel test execution with `t.Parallel()`
|
||||
- Isolated subtests with `t.Run()`
|
||||
|
||||
## Test Helpers
|
||||
|
||||
Use `t.Helper()` to mark helper functions:
|
||||
|
||||
```go
|
||||
func assertNoError(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func assertEqual(t *testing.T, got, want interface{}) {
|
||||
t.Helper()
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Correct line numbers in test failures
|
||||
- Reusable test utilities
|
||||
- Cleaner test code
|
||||
|
||||
## Test Fixtures
|
||||
|
||||
Use `t.Cleanup()` for resource cleanup:
|
||||
|
||||
```go
|
||||
func testDB(t *testing.T) *sql.DB {
|
||||
t.Helper()
|
||||
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open test db: %v", err)
|
||||
}
|
||||
|
||||
// Cleanup runs after test completes
|
||||
t.Cleanup(func() {
|
||||
if err := db.Close(); err != nil {
|
||||
t.Errorf("failed to close db: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func TestUserRepository(t *testing.T) {
|
||||
db := testDB(t)
|
||||
repo := NewUserRepository(db)
|
||||
// ... test logic
|
||||
}
|
||||
```
|
||||
|
||||
## Race Detection
|
||||
|
||||
Always run tests with the `-race` flag to detect data races:
|
||||
|
||||
```bash
|
||||
go test -race ./...
|
||||
```
|
||||
|
||||
**In CI/CD:**
|
||||
```yaml
|
||||
- name: Test with race detector
|
||||
run: go test -race -timeout 5m ./...
|
||||
```
|
||||
|
||||
**Why:**
|
||||
- Detects concurrent access bugs
|
||||
- Prevents production race conditions
|
||||
- Minimal performance overhead in tests
|
||||
|
||||
## Coverage Analysis
|
||||
|
||||
### Basic Coverage
|
||||
|
||||
```bash
|
||||
go test -cover ./...
|
||||
```
|
||||
|
||||
### Detailed Coverage Report
|
||||
|
||||
```bash
|
||||
go test -coverprofile=coverage.out ./...
|
||||
go tool cover -html=coverage.out
|
||||
```
|
||||
|
||||
### Coverage Thresholds
|
||||
|
||||
```bash
|
||||
# Fail if coverage below 80%
|
||||
go test -cover ./... | grep -E 'coverage: [0-7][0-9]\.[0-9]%' && exit 1
|
||||
```
|
||||
|
||||
## Benchmarking
|
||||
|
||||
```go
|
||||
func BenchmarkValidateEmail(b *testing.B) {
|
||||
email := "user@example.com"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ValidateEmail(email)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Run benchmarks:**
|
||||
```bash
|
||||
go test -bench=. -benchmem
|
||||
```
|
||||
|
||||
**Compare benchmarks:**
|
||||
```bash
|
||||
go test -bench=. -benchmem > old.txt
|
||||
# make changes
|
||||
go test -bench=. -benchmem > new.txt
|
||||
benchstat old.txt new.txt
|
||||
```
|
||||
|
||||
## Mocking
|
||||
|
||||
### Interface-Based Mocking
|
||||
|
||||
```go
|
||||
type UserRepository interface {
|
||||
GetUser(id string) (*User, error)
|
||||
}
|
||||
|
||||
type mockUserRepository struct {
|
||||
users map[string]*User
|
||||
err error
|
||||
}
|
||||
|
||||
func (m *mockUserRepository) GetUser(id string) (*User, error) {
|
||||
if m.err != nil {
|
||||
return nil, m.err
|
||||
}
|
||||
return m.users[id], nil
|
||||
}
|
||||
|
||||
func TestUserService(t *testing.T) {
|
||||
mock := &mockUserRepository{
|
||||
users: map[string]*User{
|
||||
"1": {ID: "1", Name: "Alice"},
|
||||
},
|
||||
}
|
||||
|
||||
service := NewUserService(mock)
|
||||
// ... test logic
|
||||
}
|
||||
```
|
||||
|
||||
## Integration Tests
|
||||
|
||||
### Build Tags
|
||||
|
||||
```go
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package user_test
|
||||
|
||||
func TestUserRepository_Integration(t *testing.T) {
|
||||
// ... integration test
|
||||
}
|
||||
```
|
||||
|
||||
**Run integration tests:**
|
||||
```bash
|
||||
go test -tags=integration ./...
|
||||
```
|
||||
|
||||
### Test Containers
|
||||
|
||||
```go
|
||||
func TestWithPostgres(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
// Setup test container
|
||||
ctx := context.Background()
|
||||
container, err := testcontainers.GenericContainer(ctx, ...)
|
||||
assertNoError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
container.Terminate(ctx)
|
||||
})
|
||||
|
||||
// ... test logic
|
||||
}
|
||||
```
|
||||
|
||||
## Test Organization
|
||||
|
||||
### File Structure
|
||||
|
||||
```
|
||||
package/
|
||||
├── user.go
|
||||
├── user_test.go # Unit tests
|
||||
├── user_integration_test.go # Integration tests
|
||||
└── testdata/ # Test fixtures
|
||||
└── users.json
|
||||
```
|
||||
|
||||
### Package Naming
|
||||
|
||||
```go
|
||||
// Black-box testing (external perspective)
|
||||
package user_test
|
||||
|
||||
// White-box testing (internal access)
|
||||
package user
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Testing HTTP Handlers
|
||||
|
||||
```go
|
||||
func TestUserHandler(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/users/1", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
handler := NewUserHandler(mockRepo)
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
assertEqual(t, rec.Code, http.StatusOK)
|
||||
}
|
||||
```
|
||||
|
||||
### Testing with Context
|
||||
|
||||
```go
|
||||
func TestWithTimeout(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
err := SlowOperation(ctx)
|
||||
if !errors.Is(err, context.DeadlineExceeded) {
|
||||
t.Errorf("expected timeout error, got %v", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `t.Parallel()`** for independent tests
|
||||
2. **Use `testing.Short()`** to skip slow tests
|
||||
3. **Use `t.TempDir()`** for temporary directories
|
||||
4. **Use `t.Setenv()`** for environment variables
|
||||
5. **Avoid `init()`** in test files
|
||||
6. **Keep tests focused** - one behavior per test
|
||||
7. **Use meaningful test names** - describe what's being tested
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Writing new Go tests
|
||||
- Improving test coverage
|
||||
- Setting up test infrastructure
|
||||
- Debugging flaky tests
|
||||
- Optimizing test performance
|
||||
- Implementing integration tests
|
||||
Reference in New Issue
Block a user