Initial commit
This commit is contained in:
255
skills/testing/SKILL.md
Normal file
255
skills/testing/SKILL.md
Normal file
@@ -0,0 +1,255 @@
|
||||
---
|
||||
name: testing
|
||||
description: Automatically invoked to write tests for new types, or use as testing expert advisor for guidance and recommendations. Covers unit, integration, and system tests with emphasis on in-memory dependencies. Use when creating leaf types, after refactoring, during implementation, or when testing advice is needed. Ensures 100% coverage on leaf types with public API testing.
|
||||
---
|
||||
|
||||
# Testing Principles
|
||||
|
||||
Principles and patterns for writing effective Go tests.
|
||||
|
||||
## When to Use
|
||||
|
||||
### Automatic Invocation (Proactive)
|
||||
- **Automatically invoked** by @linter-driven-development during Phase 2 (Implementation)
|
||||
- **Automatically invoked** by @refactoring when new isolated types are created
|
||||
- **Automatically invoked** by @code-designing after designing new types
|
||||
- **After creating new leaf types** - Types that should have 100% unit test coverage
|
||||
- **After extracting functions** during refactoring that create testable units
|
||||
|
||||
### Manual Invocation
|
||||
- User explicitly requests tests to be written
|
||||
- User asks for testing advice, recommendations, or "what to do"
|
||||
- When testing strategy is unclear (table-driven vs testify suites)
|
||||
- When choosing between dependency levels (in-memory vs binary vs test-containers)
|
||||
- When adding tests to existing untested code
|
||||
- When user needs testing expert guidance or consultation
|
||||
|
||||
**IMPORTANT**: This skill writes tests autonomously based on the code structure and type design, and also serves as a testing expert advisor
|
||||
|
||||
## Testing Philosophy
|
||||
|
||||
**Test only the public API**
|
||||
- Use `pkg_test` package name
|
||||
- Test types through their constructors
|
||||
- No testing private methods/functions
|
||||
|
||||
**Prefer real implementations over mocks**
|
||||
- Use in-memory implementations (fastest, no external deps)
|
||||
- Use HTTP test servers (httptest)
|
||||
- Use temp files/directories
|
||||
- Test with actual dependencies when beneficial
|
||||
|
||||
**Coverage targets**
|
||||
- Leaf types: 100% unit test coverage
|
||||
- Orchestrating types: Integration tests
|
||||
- Critical workflows: System tests
|
||||
|
||||
## Test Pyramid
|
||||
|
||||
Three levels of testing, each serving a specific purpose:
|
||||
|
||||
**Unit Tests** (Base of pyramid - most tests here)
|
||||
- Test leaf types in isolation
|
||||
- Fast, focused, no external dependencies
|
||||
- 100% coverage target for leaf types
|
||||
- Use `pkg_test` package, test public API only
|
||||
|
||||
**Integration Tests** (Middle - fewer than unit)
|
||||
- Test seams between components
|
||||
- Test workflows across package boundaries
|
||||
- Use real or in-memory implementations
|
||||
- Verify components work together correctly
|
||||
|
||||
**System Tests** (Top - fewest tests)
|
||||
- Black box testing from `tests/` folder
|
||||
- Test entire system via CLI/API
|
||||
- Test critical end-to-end workflows
|
||||
- **Strive for independence in Go** (minimize external deps)
|
||||
|
||||
## Reusable Test Infrastructure
|
||||
|
||||
Build shared test infrastructure in `internal/testutils/`:
|
||||
- In-memory mock servers with DSL (HTTP, DB, file system)
|
||||
- Reusable across all test levels
|
||||
- Test the infrastructure itself!
|
||||
- Can expose as CLI tools for manual testing
|
||||
|
||||
**Dependency Priority** (minimize external dependencies):
|
||||
1. **In-memory** (preferred): Pure Go, httptest, in-memory DB
|
||||
2. **Binary**: Standalone executable via exec.Command
|
||||
3. **Test-containers**: Programmatic Docker from Go
|
||||
4. **Docker-compose**: Last resort, manual testing only
|
||||
|
||||
Goal: System tests should be **independent in Go** when possible.
|
||||
|
||||
See reference.md for comprehensive testutils patterns and DSL examples.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Unit Tests Workflow
|
||||
|
||||
**Purpose**: Test leaf types in isolation, 100% coverage target
|
||||
|
||||
1. **Identify leaf types** - Self-contained types with logic
|
||||
2. **Choose structure** - Table-driven (simple) or testify suites (complex setup)
|
||||
3. **Write in pkg_test package** - Test public API only
|
||||
4. **Use in-memory implementations** - From testutils or local implementations
|
||||
5. **Avoid pitfalls** - No time.Sleep, no conditionals in cases, no private method tests
|
||||
|
||||
**Test structure:**
|
||||
- Table-driven: Separate success/error test functions (complexity = 1)
|
||||
- Testify suites: Only for complex infrastructure setup (HTTP servers, DBs)
|
||||
- Always use named struct fields (linter reorders fields)
|
||||
|
||||
See reference.md for detailed patterns and examples.
|
||||
|
||||
### Integration Tests Workflow
|
||||
|
||||
**Purpose**: Test seams between components, verify they work together
|
||||
|
||||
1. **Identify integration points** - Where packages/components interact
|
||||
2. **Choose dependencies** - Prefer: in-memory > binary > test-containers
|
||||
3. **Write tests** - In `pkg_test` or `integration_test.go` with build tags
|
||||
4. **Test workflows** - Cover happy path and error scenarios across boundaries
|
||||
5. **Use real or testutils implementations** - Avoid heavy mocking
|
||||
|
||||
**File organization:**
|
||||
```go
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
// Test Service + Repository + real/mock dependencies
|
||||
```
|
||||
|
||||
See reference.md for integration test patterns with dependencies.
|
||||
|
||||
### System Tests Workflow
|
||||
|
||||
**Purpose**: Black box test entire system, critical end-to-end workflows
|
||||
|
||||
1. **Place in tests/ folder** - At project root, separate from packages
|
||||
2. **Test via CLI/API** - exec.Command for CLI, HTTP client for APIs
|
||||
3. **Minimize external deps** - Prefer: in-memory mocks > binary > test-containers
|
||||
4. **Strive for Go independence** - Pure Go tests, no Docker when possible
|
||||
5. **Test critical workflows** - User journeys, not every edge case
|
||||
|
||||
**Example structure:**
|
||||
```go
|
||||
// tests/cli_test.go
|
||||
func TestCLI_UserWorkflow(t *testing.T) {
|
||||
mockAPI := testutils.NewMockServer().
|
||||
OnGET("/users/1").RespondJSON(200, user).
|
||||
Build() // In-memory httptest.Server
|
||||
defer mockAPI.Close()
|
||||
|
||||
cmd := exec.Command("./myapp", "get-user", "1",
|
||||
"--api-url", mockAPI.URL())
|
||||
output, err := cmd.CombinedOutput()
|
||||
// Assert on output
|
||||
}
|
||||
```
|
||||
|
||||
See reference.md for comprehensive system test patterns.
|
||||
|
||||
## Key Test Patterns
|
||||
|
||||
**Table-Driven Tests:**
|
||||
- Separate success and error test functions (complexity = 1)
|
||||
- Always use named struct fields (linter reorders fields)
|
||||
- No wantErr bool pattern (adds conditionals)
|
||||
|
||||
**Testify Suites:**
|
||||
- Only for complex infrastructure (HTTP servers, DBs, OpenTelemetry)
|
||||
- SetupSuite/TearDownSuite for expensive shared setup
|
||||
- SetupTest/TearDownTest for per-test isolation
|
||||
|
||||
**Synchronization:**
|
||||
- Never use time.Sleep (flaky, slow)
|
||||
- Use channels with select/timeout for async operations
|
||||
- Use sync.WaitGroup for concurrent operations
|
||||
|
||||
See reference.md for complete patterns with code examples.
|
||||
|
||||
## Output Format
|
||||
|
||||
After writing tests:
|
||||
|
||||
```
|
||||
✅ TESTING COMPLETE
|
||||
|
||||
📊 Unit Tests:
|
||||
- user/user_id_test.go: 100% (4 test cases)
|
||||
- user/email_test.go: 100% (6 test cases)
|
||||
- user/service_test.go: 100% (8 test cases)
|
||||
|
||||
🔗 Integration Tests:
|
||||
- user/integration_test.go: 3 workflows tested
|
||||
- Dependencies: In-memory DB, httptest mock server
|
||||
|
||||
🎯 System Tests:
|
||||
- tests/cli_test.go: 2 end-to-end workflows
|
||||
- tests/api_test.go: 1 full API workflow
|
||||
- Infrastructure: In-memory mocks (pure Go, no Docker)
|
||||
|
||||
Test Infrastructure:
|
||||
- internal/testutils/httpserver: In-memory mock API with DSL
|
||||
- internal/testutils/mockdb: In-memory database mock
|
||||
|
||||
Test Execution:
|
||||
$ go test ./... # All tests
|
||||
$ go test -tags=integration ./... # Include integration tests
|
||||
$ go test ./tests/... # System tests only
|
||||
|
||||
✅ All tests pass
|
||||
✅ 100% coverage on leaf types
|
||||
✅ No external dependencies required
|
||||
|
||||
Next Steps:
|
||||
1. Run linter: task lintwithfix
|
||||
2. If linter fails → use @refactoring skill
|
||||
3. If linter passes → use @pre-commit-review skill
|
||||
```
|
||||
|
||||
## Key Principles
|
||||
|
||||
See reference.md for:
|
||||
- Table-driven test patterns
|
||||
- Testify suite guidelines
|
||||
- Real implementations over mocks
|
||||
- Synchronization techniques
|
||||
- Coverage strategies
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Unit Tests
|
||||
- [ ] All unit tests in pkg_test package
|
||||
- [ ] Testing public API only (no private methods)
|
||||
- [ ] Table-driven tests use named struct fields
|
||||
- [ ] No conditionals in test cases (complexity = 1)
|
||||
- [ ] Using in-memory implementations from testutils
|
||||
- [ ] No time.Sleep (using channels/waitgroups)
|
||||
- [ ] Leaf types have 100% coverage
|
||||
|
||||
### Integration Tests
|
||||
- [ ] Test seams between components
|
||||
- [ ] Use in-memory or binary dependencies (avoid Docker)
|
||||
- [ ] Build tags for optional execution (`//go:build integration`)
|
||||
- [ ] Cover happy path and error scenarios across boundaries
|
||||
- [ ] Real or testutils implementations (minimal mocking)
|
||||
|
||||
### System Tests
|
||||
- [ ] Located in tests/ folder at project root
|
||||
- [ ] Black box testing via CLI/API
|
||||
- [ ] Uses in-memory testutils mocks (pure Go)
|
||||
- [ ] No external dependencies (no Docker required)
|
||||
- [ ] Tests critical end-to-end workflows
|
||||
- [ ] Fast execution, runs in CI without setup
|
||||
|
||||
### Test Infrastructure
|
||||
- [ ] Reusable mocks in internal/testutils/
|
||||
- [ ] Test infrastructure has its own tests
|
||||
- [ ] DSL provides readable test setup
|
||||
- [ ] Can be exposed as CLI for manual testing
|
||||
|
||||
See reference.md for complete testing guidelines and examples.
|
||||
Reference in New Issue
Block a user