evals/evals.json
[
{
"id": 1,
"name": "goleak-goroutine-leak-detection",
"description": "Tests use goleak for goroutine leak detection, not just task completion",
"prompt": "Write tests for a `workerpool` package. The package has a `Pool` struct with `Start(numWorkers int)`, `Submit(task func())`, and `Stop()` methods. Start spawns goroutines, Submit enqueues work, Stop shuts down gracefully. Write comprehensive unit tests covering start, submit tasks, and stop.",
"trap": "Model writes normal unit tests verifying task completion but omits goroutine leak detection — Stop() may appear to work while leaking goroutines",
"assertions": [
{
"id": "1.1",
"text": "Uses goleak (go.uber.org/goleak) — either goleak.VerifyTestMain in TestMain or goleak.VerifyNone per-test — to detect goroutine leaks from the worker pool"
},
{
"id": "1.2",
"text": "Has a TestMain function if using goleak.VerifyTestMain (the package-level approach)"
},
{
"id": "1.3",
"text": "Tests verify that Stop() properly cleans up goroutines (not just that tasks complete)"
},
{
"id": "1.4",
"text": "Imports go.uber.org/goleak"
}
]
},
{
"id": 2,
"name": "integration-build-tag-not-testing-short",
"description": "Integration tests use //go:build integration tag; testing.Short() is not an acceptable alternative",
"prompt": "Our team disagrees on how to separate integration tests from unit tests in our Go project. A teammate proposes:\n\n```go\nfunc TestUserRepository_Create(t *testing.T) {\n if testing.Short() {\n t.Skip(\"skipping integration test\")\n }\n db := connectToPostgres(t)\n // ... test ...\n}\n```\n\nThey argue: 'testing.Short() is the Go standard way — it's in the stdlib, you can configure it with -short, and every Go developer knows it. Build tags are extra complexity for no benefit.'\n\nHow should integration tests be separated? Is the teammate's approach correct? Write the correct implementation for a TestUserRepository_Create integration test.",
"trap": "Model accepts testing.Short() as a valid approach because it's a stdlib feature and the teammate's argument sounds reasonable. The skill teaches: build tags are required because testing.Short() still compiles tests into the binary, requires a flag to skip, and leaks DB connection attempts into normal test runs.",
"assertions": [
{
"id": "2.1",
"text": "Rejects testing.Short() as the primary separation mechanism — does not accept the teammate's approach as correct"
},
{
"id": "2.2",
"text": "Uses `//go:build integration` build tag (at the file level, before the package declaration)"
},
{
"id": "2.3",
"text": "Explains why build tags are preferred: tests using testing.Short() still compile and attempt connections when running without -short, whereas build-tagged files are completely excluded from compilation"
},
{
"id": "2.4",
"text": "Includes the command to run integration tests: go test -tags=integration ./..."
}
]
},
{
"id": 3,
"name": "parallel-subtests-pure-function",
"description": "Pure function subtests call t.Parallel(); top-level test also parallel",
"prompt": "Write table-driven tests for a pure function `Slugify(input string) string` that converts titles to URL-friendly slugs (lowercase, hyphens for spaces, strips special chars). Test at least 6 cases: normal title, unicode, multiple spaces, empty string, already-slugified input, and special characters only.",
"trap": "Model omits t.Parallel() since the function is pure and 'already fast enough', missing parallelism opportunities for stateless tests",
"assertions": [
{
"id": "3.1",
"text": "Subtests call t.Parallel() — these are independent pure function tests with no shared mutable state"
},
{
"id": "3.2",
"text": "Top-level test function also calls t.Parallel()"
},
{
"id": "3.3",
"text": "Each test case has a descriptive `name` field used in t.Run"
},
{
"id": "3.4",
"text": "At least 6 test cases as requested"
},
{
"id": "3.5",
"text": "No shared mutable state between subtests (each subtest captures its own test case variable)"
}
]
},
{
"id": 4,
"name": "fake-clock-injection-for-time-dependent-tests",
"description": "Time-dependent code must accept a clock interface so tests can use clockwork.FakeClock; real time.Sleep is unacceptable",
"prompt": "Here is an existing RateLimiter implementation:\n\n```go\ntype RateLimiter struct {\n limit int\n window time.Duration\n count int\n resetAt time.Time\n}\n\nfunc NewRateLimiter(limit int, window time.Duration) *RateLimiter {\n return &RateLimiter{\n limit: limit,\n window: window,\n resetAt: time.Now().Add(window),\n }\n}\n\nfunc (r *RateLimiter) Allow() bool {\n now := time.Now()\n if now.After(r.resetAt) {\n r.count = 0\n r.resetAt = now.Add(r.window)\n }\n if r.count >= r.limit {\n return false\n }\n r.count++\n return true\n}\n```\n\nWrite tests that verify:\n1. Allow() returns true while under the limit\n2. Allow() returns false when the limit is exceeded\n3. The counter resets after the time window expires\n\nThe tests must run in milliseconds, not seconds. You may modify the implementation if needed.",
"trap": "Model uses time.Sleep(window + small margin) to test window expiration — the code uses time.Now() directly, making tests slow and flaky. The skill teaches to refactor the code to accept a clock interface (clockwork.Clock) and inject a FakeClock in tests.",
"assertions": [
{
"id": "4.1",
"text": "Modifies the RateLimiter to accept a clock interface (e.g., clockwork.Clock or a custom Now() func) rather than calling time.Now() directly"
},
{
"id": "4.2",
"text": "Uses clockwork.FakeClock (or equivalent) in tests to advance time without real sleeping — tests run in microseconds"
},
{
"id": "4.3",
"text": "Tests the window reset scenario by advancing the fake clock past the window duration (e.g., fakeClock.Advance(window + time.Millisecond))"
},
{
"id": "4.4",
"text": "No real-time time.Sleep in test code; use synctest.Test/synctest.Wait or a fake clock for deterministic synthetic time"
}
]
},
{
"id": 5,
"name": "consumer-site-interface-mocking",
"description": "Tests define interfaces at the consumer site and mock those, not concrete structs",
"prompt": "Test a `NotificationService` struct that has a `NotifyUser(userID string) error` method. It depends on two concrete structs: `SMTPClient` (with `Send(to, subject, body string) error`) and `AuditLogger` (with `Log(event string) error`). NotifyUser looks up the user's email, sends an email via SMTPClient, and logs the event via AuditLogger. Write comprehensive tests for NotifyUser.",
"trap": "Model embeds or wraps concrete SMTPClient/AuditLogger in mock structs, or creates test doubles that shadow the concrete types, instead of extracting consumer-site interfaces",
"assertions": [
{
"id": "5.1",
"text": "Defines interfaces for the dependencies (e.g., EmailSender, Logger) rather than using the concrete SMTPClient/AuditLogger structs directly in tests"
},
{
"id": "5.2",
"text": "Creates mock implementations of these interfaces (using testify/mock or manual mocks)"
},
{
"id": "5.3",
"text": "Does NOT embed or wrap the concrete SMTPClient/AuditLogger structs in mock objects"
},
{
"id": "5.4",
"text": "Uses dependency injection — NotificationService accepts interfaces, not concrete types"
},
{
"id": "5.5",
"text": "Tests verify both happy path (send succeeds) and error scenarios (send fails, log fails)"
}
]
},
{
"id": 6,
"name": "test-observable-behavior-not-internals",
"description": "Tests verify behavior via public API only, not by inspecting the internal map field",
"prompt": "Test a `UserCache` struct with `Get(id string) (*User, bool)`, `Set(id string, user *User)`, and `Len() int` methods. The cache has an internal `data map[string]*User` field. Write tests that verify the cache stores and retrieves users correctly. Make sure to verify the internal map state is consistent after each operation.",
"trap": "Model directly accesses the internal data map field to verify state, coupling tests to implementation details",
"assertions": [
{
"id": "6.1",
"text": "Tests observable behavior through Get/Set/Len public API only"
},
{
"id": "6.2",
"text": "Does NOT directly access or inspect the internal `data` map field"
},
{
"id": "6.3",
"text": "Does NOT use same-package (white-box) testing to examine cache internals"
},
{
"id": "6.4",
"text": "Tests cover cache hit, cache miss, overwrite, and Len() correctness"
}
]
},
{
"id": 7,
"name": "external-test-package-resists-internal-access",
"description": "Tests use package_test to black-box test; does not access unexported state even when a teammate suggests it",
"prompt": "Write tests for a `tokenizer` package that exports `Tokenize(input string) []Token` and a `Token` struct with `Type` and `Value` fields. The package has an unexported `tokenCache map[string][]Token` field on the package level for memoization.\n\nA teammate says: 'We should verify the cache is being populated correctly — access tokenCache directly in the tests. It's in the same package so it's fine, and it makes the tests more reliable since we can confirm memoization is working.'\n\nWrite the tests. Should you follow the teammate's suggestion?",
"trap": "Model follows the teammate's suggestion and uses package tokenizer (white-box) to access tokenCache directly, reasoning that it makes tests more reliable. The skill teaches to use package tokenizer_test and test only observable behavior — cache behavior is an implementation detail, not a contract.",
"assertions": [
{
"id": "7.1",
"text": "Rejects the teammate's suggestion — does not access tokenCache directly in tests"
},
{
"id": "7.2",
"text": "Uses `package tokenizer_test` (external test package) instead of `package tokenizer`"
},
{
"id": "7.3",
"text": "Explains why: cache behavior is an implementation detail — tests should verify the observable contract (same input → same output), not whether a specific optimization is in place"
},
{
"id": "7.4",
"text": "Tests focus on functional correctness: same input tokenizes identically on repeated calls, edge cases, different token types"
}
]
},
{
"id": 8,
"name": "example-functions-as-documentation",
"description": "Packages need Example functions with // Output: comments that serve as executable documentation in go doc",
"prompt": "I've written a `currency` package with these functions:\n\n```go\nfunc Format(cents int64, code string) string // Format(1234, \"USD\") → \"$12.34\"\nfunc Parse(s string) (int64, string, error) // Parse(\"$12.34\") → 1234, \"USD\", nil\nfunc Convert(cents int64, from, to string, rate float64) int64\n```\n\nA colleague says: 'The functions are self-explanatory — names and signatures are clear enough. We don't need extra documentation. Just write unit tests with good coverage.'\n\nWrite comprehensive test coverage for this package. Should you follow the colleague's advice about documentation?",
"trap": "Model follows the colleague's advice and writes only table-driven unit tests without Example functions — missing the executable documentation that shows real usage in `go doc` and on pkg.go.dev. The skill teaches that Example functions serve as both tests and documentation.",
"assertions": [
{
"id": "8.1",
"text": "Disagrees with the colleague — includes Example functions despite the advice to skip them"
},
{
"id": "8.2",
"text": "Includes at least one Example function (ExampleFormat, ExampleParse, or ExampleConvert)"
},
{
"id": "8.3",
"text": "Example functions have `// Output:` comments so they are verified by go test"
},
{
"id": "8.4",
"text": "Explains that Example functions serve as executable documentation visible in go doc and pkg.go.dev — not just tests"
}
]
},
{
"id": 9,
"name": "fuzz-test-for-critical-functions",
"description": "Security-critical functions get fuzz tests with seed corpus and property assertions",
"prompt": "Write tests for a `SanitizeHTML(input string) string` function that strips all HTML tags from input while preserving text content. Make sure to test edge cases thoroughly — this function is critical for security.",
"trap": "Model writes only table-driven tests for known edge cases, missing the fuzz test that would discover unexpected inputs causing XSS vulnerabilities",
"assertions": [
{
"id": "9.1",
"text": "Includes a fuzz test function (FuzzSanitizeHTML or similar)"
},
{
"id": "9.2",
"text": "Fuzz test uses f.Add() to provide seed corpus entries"
},
{
"id": "9.3",
"text": "Fuzz test includes property-based assertions (e.g., output contains no < or > characters, or double-sanitize is idempotent)"
},
{
"id": "9.4",
"text": "Also includes regular table-driven tests for known edge cases"
},
{
"id": "9.5",
"text": "Table tests cover tricky cases like nested tags, unclosed tags, or script tags"
}
]
},
{
"id": 10,
"name": "test-helper-t-helper-attribution",
"description": "Test helpers must call t.Helper() so failures point to the caller's line, not the helper's internal line",
"prompt": "I have this test helper and some tests using it:\n\n```go\nfunc requireNoError(t *testing.T, err error, msg string) {\n if err != nil {\n t.Fatalf(\"%s: unexpected error: %v\", msg, err)\n }\n}\n\nfunc TestProcessOrder(t *testing.T) {\n order := NewOrder(\"prod-1\", 2)\n err := order.Validate()\n requireNoError(t, err, \"validate\")\n\n err = order.Submit()\n requireNoError(t, err, \"submit\")\n}\n```\n\nWhen Validate() fails, the test output reports a failure at the `t.Fatalf` line inside `requireNoError`, not at the `requireNoError(t, err, \"validate\")` call site in `TestProcessOrder`. Is this a problem? How do you fix it?",
"trap": "Model says this is expected behavior or suggests switching to t.Error() instead of the real fix. The skill teaches that t.Helper() must be called as the first statement in the helper so Go's test framework reports failures at the caller's line.",
"assertions": [
{
"id": "10.1",
"text": "Identifies this as a real problem — the line number pointing to the helper's internal Fatalf is unhelpful for debugging which call caused the failure"
},
{
"id": "10.2",
"text": "Fixes it by adding t.Helper() as the first statement in requireNoError — not by restructuring the helper or using a different assertion method"
},
{
"id": "10.3",
"text": "Explains that t.Helper() marks the function as a test helper so that the testing framework reports the caller's file:line instead of the helper's file:line"
},
{
"id": "10.4",
"text": "Does NOT suggest switching to t.Error() as the fix — t.Helper() is the correct solution regardless of t.Fatal vs t.Error"
}
]
},
{
"id": 11,
"name": "httptest-recorder-not-real-server",
"description": "HTTP handler tests use httptest.NewRecorder, not a real HTTP server",
"prompt": "Write end-to-end tests for a REST API handler `HandleCreateOrder(w http.ResponseWriter, r *http.Request)` that accepts POST with JSON body `{\"product\": \"...\", \"quantity\": N}`. It returns 201 with the order JSON on success, 400 for invalid JSON, and 422 for validation errors (empty product, quantity <= 0). Test it like a real client would call it.",
"trap": "Model starts a real HTTP server with httptest.NewServer or net/http ListenAndServe, adding unnecessary network overhead and port allocation to tests",
"assertions": [
{
"id": "11.1",
"text": "Uses httptest.NewRecorder (not httptest.NewServer or a real HTTP server)"
},
{
"id": "11.2",
"text": "Table-driven with named test cases covering multiple scenarios"
},
{
"id": "11.3",
"text": "Tests at least 3 status codes (201, 400, 422)"
},
{
"id": "11.4",
"text": "Verifies response body content (not just status code)"
},
{
"id": "11.5",
"text": "Sets proper Content-Type header on requests"
}
]
},
{
"id": 12,
"name": "testify-suite-for-integration",
"description": "Integration tests use testify/suite with SetupSuite/TearDownTest for organized setup/teardown",
"prompt": "Write integration tests for an `OrderRepository` that interacts with PostgreSQL. It has `Create(order *Order) error`, `GetByID(id string) (*Order, error)`, and `ListByUserID(userID string) ([]*Order, error)`. Tests need database setup (create tables), per-test data cleanup, and graceful teardown. Organize them cleanly so setup/teardown happens automatically. These must not run during normal unit tests.",
"trap": "Model uses TestMain or plain setup functions with defer for teardown, mixing setup concerns into each test instead of a suite",
"assertions": [
{
"id": "12.1",
"text": "Uses testify/suite.Suite struct embedding for test organization"
},
{
"id": "12.2",
"text": "Has SetupSuite (or similar) for one-time database connection and schema setup"
},
{
"id": "12.3",
"text": "Has SetupTest or TearDownTest for per-test data cleanup (e.g., TRUNCATE)"
},
{
"id": "12.4",
"text": "Has TearDownSuite for graceful shutdown (close DB, docker-compose down)"
},
{
"id": "12.5",
"text": "Uses `//go:build integration` build tag"
},
{
"id": "12.6",
"text": "Has a runner function `func TestXxx(t *testing.T) { suite.Run(t, ...) }`"
}
]
},
{
"id": 13,
"name": "benchmark-report-allocs-and-input-sizes",
"description": "Benchmarks use b.ReportAllocs(), test multiple input sizes, and follow naming conventions",
"prompt": "Write benchmarks for a `Compress(data []byte) ([]byte, error)` function that compresses byte slices. We need to measure performance to decide if this is fast enough for our hot path. Just write the benchmark tests.",
"trap": "Model writes a single benchmark with one input size and omits b.ReportAllocs(), missing allocation tracking and size-scaling analysis",
"assertions": [
{
"id": "13.1",
"text": "Calls b.ReportAllocs() to track memory allocations per operation"
},
{
"id": "13.2",
"text": "Tests multiple input sizes using b.Run with descriptive sub-benchmark names (e.g., size=1KB, size=1MB)"
},
{
"id": "13.3",
"text": "Uses b.Loop() for Go 1.24+ benchmark loops; uses legacy b.N only for older module targets"
},
{
"id": "13.4",
"text": "Follows benchmark naming convention: BenchmarkCompress or BenchmarkCompress_<variant>"
},
{
"id": "13.5",
"text": "Prevents compiler optimization of the result (assigns to a package-level variable or uses _ =)"
},
{
"id": "13.6",
"text": "Does NOT include setup/allocation costs inside the timed loop (or uses b.ResetTimer if setup is needed)"
}
]
},
{
"id": 14,
"name": "race-detection-and-test-independence",
"description": "Tests for concurrent code include -race flag guidance and ensure test independence (no order dependence)",
"prompt": "Write tests for a `SafeMap[K comparable, V any]` struct that provides a goroutine-safe map with `Get(key K) (V, bool)`, `Set(key K, value V)`, `Delete(key K)`, and `Len() int` methods. Multiple goroutines will call these concurrently. Write thorough tests including concurrent access scenarios. Also include a note on how to run these tests in CI.",
"trap": "Model writes concurrent tests but omits -race flag guidance for CI and doesn't ensure tests are independently runnable (e.g., shares map state between test functions)",
"assertions": [
{
"id": "14.1",
"text": "Includes concurrent test scenarios where multiple goroutines call Get/Set/Delete simultaneously"
},
{
"id": "14.2",
"text": "Recommends running with -race flag (go test -race) for CI or includes it in a run command comment"
},
{
"id": "14.3",
"text": "Each test function creates its own SafeMap instance — no shared state between test functions"
},
{
"id": "14.4",
"text": "Uses sync.WaitGroup or similar synchronization to coordinate concurrent test goroutines"
},
{
"id": "14.5",
"text": "Tests are independently runnable (any single test can pass when run in isolation with -run)"
}
]
}
]
references/benchmarks.md
# Benchmarks in a Test Suite
Benchmarking methodology — `benchstat`, profiling from benchmarks, noise control, CI regression detection — belongs to the `samber/cc-skills-golang@golang-benchmark` skill. This page only covers writing a benchmark that sits next to the tests of the same package.
## Shape
```go
func BenchmarkStringConcatenation(b *testing.B) {
b.Run("plus-operator", func(b *testing.B) {
for b.Loop() {
result := "a" + "b" + "c"
_ = result
}
})
b.Run("strings.Builder", func(b *testing.B) {
for b.Loop() {
var builder strings.Builder
builder.WriteString("a")
builder.WriteString("b")
builder.WriteString("c")
_ = builder.String()
}
})
}
```
Sub-benchmarks give each variant its own name in the output, which is what `benchstat` compares. A single benchmark mixing both variants produces one number and hides the difference.
## Varying input size
```go
func BenchmarkFibonacci(b *testing.B) {
sizes := []int{10, 20, 30}
for _, size := range sizes {
b.Run(fmt.Sprintf("n=%d", size), func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
Fibonacci(size)
}
})
}
}
```
Size-parameterized sub-benchmarks expose complexity growth: a jump that outpaces the size increase points at a superlinear algorithm, which no single-size benchmark reveals.
## `b.Loop()` vs `b.N`
For Go 1.24+, write new benchmarks with `b.Loop()` — it keeps setup outside the timed region and prevents the compiler from optimizing the loop body away, the two failure modes that make `b.N` benchmarks report impossibly fast results. Use a legacy `b.N` loop only when the module targets Go <1.24 or when preserving existing benchmark code intentionally.
→ See `samber/cc-skills-golang@golang-benchmark` skill for measurement methodology and regression detection.
references/coverage.md
# Code Coverage
Coverage measures which lines ran, not whether their behavior was asserted. Treat it as a gap finder — read the uncovered lines — not as a quality target to chase.
## Commands
```bash
# Generate coverage file
go test -coverprofile=coverage.out ./...
# View coverage in HTML (uncovered lines in red)
go tool cover -html=coverage.out
# Coverage by function
go tool cover -func=coverage.out
# Total coverage percentage
go tool cover -func=coverage.out | grep total
# Count how many times each statement ran, not just whether it ran
go test -covermode=count -coverprofile=coverage.out ./...
# Safe under -race (atomic counters)
go test -race -covermode=atomic -coverprofile=coverage.out ./...
# Attribute coverage of package A to tests living in package B
go test -coverpkg=./... ./...
# Coverage of a single package, printed inline
go test -cover ./internal/store
```
## Modes
| Mode | Records | Use when |
| --- | --- | --- |
| `set` | Statement executed (default) | Normal runs |
| `count` | Execution count per statement | Finding never-taken branches in hot paths |
| `atomic` | Count, race-safe | Any run combined with `-race` or `t.Parallel()` |
## Pitfalls
- **Per-package by default.** Without `-coverpkg`, a test in `api` exercising `store` reports nothing for `store`, making well-tested packages look untested.
- **Integration tests are invisible** unless the build tag is passed: `go test -tags=integration -coverprofile=...`.
- **Generated code inflates the number.** Exclude it before setting any threshold, otherwise the metric measures the generator.
- **A covered line is not an asserted line.** A test that calls a function and ignores its result reports 100% coverage and verifies nothing.
→ See `samber/cc-skills-golang@golang-continuous-integration` skill for wiring coverage reporting into CI.
references/examples.md
# Examples as Documentation
Examples are executable documentation: `go test` runs them and compares stdout to the `// Output:` comment, and `pkg.go.dev` renders them next to the documented symbol. An example that drifts from the API fails the build, unlike a code block in a README.
```go
func ExampleCalculatePrice() {
price := CalculatePrice(100, 10.0)
fmt.Printf("Price: %.2f\n", price)
// Output: Price: 900.00
}
func ExampleCalculatePrice_singleItem() {
price := CalculatePrice(1, 25.50)
fmt.Printf("Price: %.2f\n", price)
// Output: Price: 25.50
}
```
## Naming
The suffix decides where godoc attaches the example, so a typo silently detaches it from its symbol:
| Function name | Documents |
| --------------------------- | ---------------------------------- |
| `Example()` | The package itself |
| `ExampleCalculatePrice()` | The `CalculatePrice` function |
| `ExampleStore_Get()` | The `Get` method of `Store` |
| `ExampleStore_Get_cached()` | A named variant of the same method |
The suffix after the second underscore MUST start with a lowercase letter — otherwise Go reads it as a type or method name and the example is orphaned.
## Output directives
- `// Output:` — stdout MUST match exactly (leading/trailing whitespace is trimmed).
- `// Unordered output:` — lines may arrive in any order. Use it for map iteration and concurrent producers, which have no stable order.
- **No output comment** — the example is compiled but not run. Useful for code that needs a live dependency, but it stops verifying behavior, so prefer a real output assertion.
## Placement
Examples live in `_test.go` files. Put them in the `package foo_test` external test package: an example that only compiles against the exported API proves the public surface is usable, which is the point of the example.
references/helpers.md
# Test Helpers
## Test Timeout
For tests that may hang, use a timeout helper that panics with caller location:
```go
// https://github.com/stretchr/testify/issues/1101
func testWithTimeout(t *testing.T, timeout time.Duration) {
t.Helper()
testFinished := make(chan struct{})
t.Cleanup(func() {
close(testFinished)
})
var pc [1]uintptr
n := runtime.Callers(2, pc[:])
line, funcName := "", ""
if n > 0 {
frames := runtime.CallersFrames(pc[:])
frame, _ := frames.Next()
line = frame.File + ":" + strconv.Itoa(frame.Line)
funcName = frame.Function
}
go func() {
select {
case <-testFinished:
case <-time.After(timeout):
panic(fmt.Sprintf("%s: Test timed out after: %v\n%s", funcName, timeout, line))
}
}()
}
// Usage
func TestLongRunningOperation(t *testing.T) {
testWithTimeout(t, 2*time.Second)
result := LongRunningOperation()
// If this takes longer than 2 seconds, the test panics with location info
}
```
references/http-testing.md
# HTTP Handler Testing
Use `httptest` package for testing HTTP handlers without starting a server.
## Basic Handler Test
```go
func TestCreateUserHandler(t *testing.T) {
tests := []struct {
name string
body string
expectedStatus int
}{
{
name: "valid request",
body: `{"name": "Alice", "email": "alice@example.com"}`,
expectedStatus: http.StatusCreated,
},
{
name: "invalid JSON",
body: `invalid json`,
expectedStatus: http.StatusBadRequest,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
is := assert.New(t)
req := httptest.NewRequest(http.MethodPost, "/users", strings.NewReader(tt.body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler := http.HandlerFunc(CreateUserHandler)
handler.ServeHTTP(w, req)
is.Equal(tt.expectedStatus, w.Code)
})
}
}
```
## Query Parameters and Headers
```go
func TestListUsersHandler(t *testing.T) {
tests := []struct {
name string
query string
authHeader string
expectedStatus int
}{
{
name: "paginated results",
query: "?page=1&limit=10",
authHeader: "Bearer token123",
expectedStatus: http.StatusOK,
},
{
name: "missing auth",
query: "?page=1",
authHeader: "",
expectedStatus: http.StatusUnauthorized,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
is := assert.New(t)
req := httptest.NewRequest(http.MethodGet, "/users"+tt.query, nil)
if tt.authHeader != "" {
req.Header.Set("Authorization", tt.authHeader)
}
w := httptest.NewRecorder()
handler := AuthMiddleware(ListUsersHandler)
handler.ServeHTTP(w, req)
is.Equal(tt.expectedStatus, w.Code)
})
}
}
```
references/integration-testing.md
# Integration Testing
## Table of Contents
- [Docker Compose Fixture](#docker-compose-fixture)
- [SQL Schema Fixture](#sql-schema-fixture)
- [Test Data Fixture](#test-data-fixture)
- [Using Fixtures in Tests](#using-fixtures-in-tests)
- [Test Helper with Embedded Fixtures](#test-helper-with-embedded-fixtures)
## Docker Compose Fixture
Create `pkg/myfeature/testdata/docker-compose.yml` for test services:
```yaml
version: "3.8"
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
ports:
- "5433:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6380:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
```
## SQL Schema Fixture
Create `pkg/myfeature/testdata/schema.sql` for database initialization:
```sql
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS orders (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
amount DECIMAL(10,2) NOT NULL,
status VARCHAR(50) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
## Test Data Fixture
Create `pkg/myfeature/testdata/testdata.sql`:
```sql
INSERT INTO users (name, email) VALUES
('Alice Johnson', 'alice@example.com'),
('Bob Smith', 'bob@example.com'),
('Charlie Brown', 'charlie@example.com');
INSERT INTO orders (user_id, amount, status) VALUES
(1, 100.00, 'completed'),
(1, 50.00, 'pending'),
(2, 200.00, 'completed');
```
## Using Fixtures in Tests
```go
//go:build integration
package database_test
import (
"database/sql"
"os"
"os/exec"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
type DatabaseTestSuite struct {
suite.Suite
db *sql.DB
}
func (s *DatabaseTestSuite) SetupSuite() {
cmd := exec.Command("docker-compose", "-f", "testdata/docker-compose.yml", "up", "-d")
if err := cmd.Run(); err != nil {
s.T().Fatalf("failed to start docker-compose: %v", err)
}
time.Sleep(5 * time.Second)
db, err := sql.Open("postgres", "postgres://test:test@localhost:5433/testdb?sslmode=disable")
if err != nil {
s.T().Fatalf("failed to connect to database: %v", err)
}
s.db = db
schema, _ := os.ReadFile("testdata/schema.sql")
_, err = db.Exec(string(schema))
if err != nil {
s.T().Fatalf("failed to run schema: %v", err)
}
}
func (s *DatabaseTestSuite) TearDownSuite() {
cmd := exec.Command("docker-compose", "-f", "testdata/docker-compose.yml", "down", "-v")
_ = cmd.Run()
}
func (s *DatabaseTestSuite) SetupTest() {
_, err := s.db.Exec("TRUNCATE TABLE orders, users CASCADE")
if err != nil {
s.T().Fatalf("failed to clear database: %v", err)
}
testdata, _ := os.ReadFile("testdata/testdata.sql")
_, err = s.db.Exec(string(testdata))
if err != nil {
s.T().Fatalf("failed to load test data: %v", err)
}
}
func (s *DatabaseTestSuite) TestUserCount() {
is := assert.New(s.T())
var count int
err := s.db.QueryRow("SELECT COUNT(*) FROM users").Scan(&count)
is.NoError(err)
is.Equal(3, count)
}
func (s *DatabaseTestSuite) TestOrderSum() {
is := assert.New(s.T())
var sum float64
err := s.db.QueryRow("SELECT SUM(amount) FROM orders").Scan(&sum)
is.NoError(err)
is.InDelta(350.0, sum, 0.01)
}
func TestDatabaseTestSuite(t *testing.T) {
suite.Run(t, new(DatabaseTestSuite))
}
```
## Test Helper with Embedded Fixtures
```go
package myfeature
import (
"database/sql"
"embed"
)
//go:embed testdata/schema.sql testdata/testdata.sql
var fixtures embed.FS
func SetupDB(db *sql.DB) error {
schema, err := fixtures.ReadFile("testdata/schema.sql")
if err != nil {
return err
}
if _, err := db.Exec(string(schema)); err != nil {
return err
}
data, err := fixtures.ReadFile("testdata/testdata.sql")
if err != nil {
return err
}
if _, err := db.Exec(string(data)); err != nil {
return err
}
return nil
}
```
references/mocking.md
# Mocking and Test Fixtures
## Table of Contents
- [Mocks with testify/mock](#mocks-with-testifymock)
- [Mock Organization](#mock-organization)
- [Test Fixtures](#test-fixtures)
- [Time Mocking](#time-mocking)
## Mocks with testify/mock
Create interfaces for your dependencies, then mock them.
> For the full testify/mock API (argument matchers, call modifiers, verification), see the `samber/cc-skills-golang@golang-stretchr-testify` skill.
```go
// Define the interface
type Database interface {
GetUser(id string) (*User, error)
CreateUser(user *User) error
}
// Mock implementation
type MockDatabase struct {
mock.Mock
}
func (m *MockDatabase) GetUser(id string) (*User, error) {
args := m.Called(id)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*User), args.Error(1)
}
func (m *MockDatabase) CreateUser(user *User) error {
args := m.Called(user)
return args.Error(0)
}
// Usage in tests
func TestService_GetUser(t *testing.T) {
is := assert.New(t)
mockDB := new(MockDatabase)
service := NewService(mockDB)
expectedUser := &User{ID: "1", Name: "John"}
mockDB.On("GetUser", "1").Return(expectedUser, nil)
user, err := service.GetUser("1")
is.NoError(err)
is.Equal(expectedUser, user)
mockDB.AssertExpectations(t)
}
func TestService_GetUser_NotFound(t *testing.T) {
is := assert.New(t)
mockDB := new(MockDatabase)
service := NewService(mockDB)
mockDB.On("GetUser", "999").Return(nil, ErrNotFound)
user, err := service.GetUser("999")
is.Error(err)
is.ErrorIs(err, ErrNotFound)
is.Nil(user)
mockDB.AssertExpectations(t)
}
```
## Mock Organization
For larger codebases, organize mocks alongside the code they mock:
```go
// user_service.go
type UserService struct {
db Database
email EmailService
}
type Database interface {
GetUser(id string) (*User, error)
CreateUser(user *User) error
}
type EmailService interface {
SendWelcomeEmail(to string) error
}
```
```go
// user_service_test.go
package mypackage_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"path/to/mypackage"
)
// MockDatabase implements mypackage.Database
type MockDatabase struct {
mock.Mock
}
func (m *MockDatabase) GetUser(id string) (*mypackage.User, error) {
args := m.Called(id)
if args.Get(0) == nil { return nil, args.Error(1) }
return args.Get(0).(*mypackage.User), args.Error(1)
}
func (m *MockDatabase) CreateUser(user *mypackage.User) error {
return m.Called(user).Error(0)
}
// MockEmailService implements mypackage.EmailService
type MockEmailService struct {
mock.Mock
}
func (m *MockEmailService) SendWelcomeEmail(to string) error {
return m.Called(to).Error(0)
}
func TestUserService_CreateUser(t *testing.T) {
mockDB := new(MockDatabase)
mockEmail := new(MockEmailService)
service := mypackage.NewUserService(mockDB, mockEmail)
user := &mypackage.User{Name: "Test", Email: "test@example.com"}
mockDB.On("CreateUser", user).Return(nil)
mockEmail.On("SendWelcomeEmail", "test@example.com").Return(nil)
err := service.CreateUser(user)
assert.NoError(t, err)
mockDB.AssertExpectations(t)
mockEmail.AssertExpectations(t)
}
```
## Test Fixtures
Create reusable test data in a separate package or file:
```go
package fixtures
import "time"
var (
DefaultUser = &User{
ID: "user-123",
Name: "Jane Doe",
Email: "jane@example.com",
CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
}
AdminUser = &User{
ID: "admin-1",
Name: "Admin User",
Email: "admin@example.com",
Role: "admin",
CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
}
)
func NewUser(name, email string) *User {
return &User{
ID: "user-" + uuid.New().String(),
Name: name,
Email: email,
CreatedAt: time.Now(),
}
}
```
## Time Mocking
Use `clockwork` to test time-dependent code without `time.Sleep()`:
```go
import (
"testing"
"time"
"github.com/jonboulle/clockwork"
"github.com/stretchr/testify/assert"
)
func TestScheduler_AddJob(t *testing.T) {
is := assert.New(t)
fakeClock := clockwork.NewFakeClock()
scheduler := NewScheduler(fakeClock)
job := &Job{ID: "1", RunAt: time.Now().Add(1 * time.Hour)}
scheduler.AddJob(job)
is.Equal(1, scheduler.PendingCount())
// Advance fake time
fakeClock.Advance(2 * time.Hour)
is.Equal(0, scheduler.PendingCount())
}
```
Install clockwork:
```bash
go get github.com/jonboulle/clockwork
```
SKILL.md
---
name: golang-testing
description: "Production-ready Golang tests — table-driven tests, testify suites and mocks, parallel tests, fuzzing, fixtures, goroutine leak detection with goleak, snapshot testing, code coverage, integration tests, idiomatic test naming. Use when writing or reviewing Go tests, choosing a testing approach, setting up Go test CI, or debugging flaky/slow tests. For testify-specific APIs see `samber/cc-skills-golang@golang-stretchr-testify`; for measurement methodology see `samber/cc-skills-golang@golang-benchmark`."
user-invocable: true
license: MIT
compatibility: Designed for Claude Code, Codex or similar harness, and for projects using Golang.
metadata:
author: samber
version: "1.4.0"
openclaw:
emoji: "🧪"
homepage: https://github.com/samber/cc-skills-golang
requires:
bins:
- go
- gotests
install:
- kind: go
package: github.com/cweill/gotests/gotests@latest
bins: [gotests]
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*) Bash(git:*) Agent Bash(gotests:*) AskUserQuestion
paths:
- "**/*.go"
---
**Persona:** You are a Go engineer who treats tests as executable specifications. You write tests to constrain behavior, not to hit coverage targets.
**Thinking mode:** Reason as thoroughly as possible for test strategy design and failure analysis — shallow reasoning misses edge cases and produces brittle tests that pass today but break tomorrow. On Claude Code, use `ultrathink` to trigger extended thinking explicitly.
**Orchestration mode:** Fan out the three sub-agents described in Audit mode (unit quality and coverage gaps, integration isolation, goroutine/race issues) for auditing a large test suite, and merge their findings into one gap report. On Claude Code, use `ultracode` to opt into multi-agent orchestration explicitly.
**Modes:**
- **Write mode** — generating new tests for existing or new code. Work sequentially through the code under test; use `gotests` to scaffold table-driven tests, then enrich with edge cases and error paths.
- **Review mode** — reviewing a PR's test changes. Focus on the diff: check coverage of new behaviour, assertion quality, table-driven structure, and absence of flakiness patterns. Sequential.
- **Audit mode** — auditing an existing test suite for gaps, flakiness, or bad patterns (order-dependent tests, missing `t.Parallel()`, implementation-detail coupling). Launch up to 3 parallel sub-agents split by concern: (1) unit test quality and coverage gaps, (2) integration test isolation and build tags, (3) goroutine leaks and race conditions.
- **Debug mode** — a test is failing or flaky. Work sequentially: reproduce reliably, isolate the failing assertion, trace the root cause in production code or test setup.
> **Community default.** A company skill that explicitly supersedes `samber/cc-skills-golang@golang-testing` skill takes precedence.
**Dependencies:**
- gotests: `go install github.com/cweill/gotests/gotests@latest`
# Go Testing Best Practices
This skill guides the creation of production-ready tests for Go applications. Follow these principles to write maintainable, fast, and reliable tests.
## Best Practices Summary
1. Table-driven tests MUST use named subtests -- every test case needs a `name` field passed to `t.Run`
2. Integration tests MUST use build tags (`//go:build integration`) to separate from unit tests
3. Tests MUST NOT depend on execution order -- each test MUST be independently runnable
4. Independent tests SHOULD use `t.Parallel()` when possible
5. Tests MUST assert observable behavior and public API contracts, not implementation details -- a test coupled to internals turns every refactor into a test rewrite while proving nothing about the contract
6. Packages with goroutines SHOULD use `goleak.VerifyTestMain` in `TestMain` to detect goroutine leaks
7. Use testify as helpers, not a replacement for standard library
8. Mock interfaces, not concrete types
9. Keep unit tests fast (< 1ms), use build tags for integration tests
10. Run tests with race detection in CI
11. Include examples as executable documentation
12. Test files MUST be named after the source file under test, not after the function or method being tested
13. Test functions SHOULD appear in the same order as the functions/methods they test in the source file
## Test Structure and Organization
### File Conventions
```go
// package_test.go - tests in same package (white-box, access unexported)
package mypackage
// mypackage_test.go - tests in test package (black-box, public API only)
package mypackage_test
```
Name the test file after the source file it tests, not after the function or method under test. Go's convention is one test file per source file (`foo.go` -> `foo_test.go`), because tools (`go test`, coverage reports, IDE "jump to test" navigation, `gotests`) and reviewers all resolve tests by source file, not by symbol. A source file usually declares several functions/methods; splitting its tests by symbol name scatters them across many files and breaks that file-to-file mapping.
```
// ✓ Good — one test file per source file
helloworld.go -> helloworld_test.go // contains TestHelloWorld, TestAbcd, TestXyz, ...
// ✗ Bad — test file named after the function/method instead of the source file
helloworld.go -> abcd_test.go // wrong: should be helloworld_test.go
```
Exception: very large source files MAY be split into multiple `_test.go` files by concern (e.g. `foo_test.go` + `foo_edgecases_test.go`), but each split file's name MUST still be derived from the source file name, never from an individual function name. Prefer keeping a single `_test.go` file per source file even when it grows large — splitting adds navigation overhead and is rarely worth it; reach for the exception only when a single file becomes genuinely unwieldy to browse or review.
Within a test file, order test functions to match the order their tested functions/methods appear in the source file. A reader (human or agent) scrolling `foo.go` alongside `foo_test.go` can then find the matching test by position instead of searching; drift between the two orderings compounds every time either file grows.
### Naming Conventions
```go
func TestAdd(t *testing.T) { ... } // function test
func TestMyStruct_MyMethod(t *testing.T) { ... } // method test
func BenchmarkAdd(b *testing.B) { ... } // benchmark
func ExampleAdd() { ... } // example
func FuzzAdd(f *testing.F) { ... } // fuzz test
```
## Table-Driven Tests
Table-driven tests are the idiomatic Go way to test multiple scenarios. Always name each test case.
```go
func TestCalculatePrice(t *testing.T) {
tests := []struct {
name string
quantity int
unitPrice float64
expected float64
}{
{
name: "single item",
quantity: 1,
unitPrice: 10.0,
expected: 10.0,
},
{
name: "bulk discount - 100 items",
quantity: 100,
unitPrice: 10.0,
expected: 900.0, // 10% discount
},
{
name: "zero quantity",
quantity: 0,
unitPrice: 10.0,
expected: 0.0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CalculatePrice(tt.quantity, tt.unitPrice)
if got != tt.expected {
t.Errorf("CalculatePrice(%d, %.2f) = %.2f, want %.2f",
tt.quantity, tt.unitPrice, got, tt.expected)
}
})
}
}
```
## Common Pitfall: Assert Scope Leaking into Subtests
Never create a testify `assert`/`require` instance in the parent test function and reuse it inside `t.Run` closures. `assert.New(t)` captures the exact `*testing.T` it was built with, so if that `t` belongs to the parent, every failure raised inside the subtest gets attributed to the _parent_ test in `go test` output — the failing subtest itself still reports `--- PASS`, silently hiding which case broke. This happens whether or not the subtest calls `t.Parallel()`.
```go
// WRONG -- `is` is bound to the parent's t
func TestCalculatePrice(t *testing.T) {
is := assert.New(t)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
is.Equal(tt.expected, CalculatePrice(tt.quantity, tt.unitPrice)) // misattributed on failure
})
}
}
// RIGHT -- each subtest builds its own instance from its own t
func TestCalculatePrice(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
is := assert.New(t)
is.Equal(tt.expected, CalculatePrice(tt.quantity, tt.unitPrice))
})
}
}
```
Verify with a deliberately-broken case: if `go test -v -run TestName` shows `--- FAIL: TestName` but every `--- PASS: TestName/subtest_name` line still says PASS, the assert scope is leaking.
## Unit Tests
Unit tests should be fast (< 1ms), isolated (no external dependencies), and deterministic.
## Testing HTTP Handlers
Use `httptest` for handler tests with table-driven patterns. See [HTTP Testing](./references/http-testing.md) for examples with request/response bodies, query parameters, headers, and status code assertions.
## Goroutine Leak Detection with goleak
Use `go.uber.org/goleak` to detect leaking goroutines, especially for concurrent code:
```go
import (
"testing"
"go.uber.org/goleak"
)
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
```
To exclude specific goroutine stacks (for known leaks or library goroutines):
```go
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m,
goleak.IgnoreCurrent(),
)
}
```
Or per-test:
```go
func TestWorkerPool(t *testing.T) {
defer goleak.VerifyNone(t)
// ... test code ...
}
```
## testing/synctest for Deterministic Goroutine Testing
`testing/synctest` (Go 1.25+) provides deterministic tests for goroutines, timers, deadlines, and context cancellation. Time advances only when all goroutines are blocked, making ordering predictable.
When to use `synctest` instead of real time:
- Testing concurrent code with time-based operations (time.Sleep, time.After, time.Ticker)
- When race conditions need to be reproducible
- When tests are flaky due to timing issues
```go
import (
"context"
"testing"
"testing/synctest"
"time"
)
func TestContextTimeout(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
const timeout = 5 * time.Second
ctx, cancel := context.WithTimeout(t.Context(), timeout)
defer cancel()
time.Sleep(timeout - time.Nanosecond)
synctest.Wait()
if err := ctx.Err(); err != nil {
t.Fatalf("before timeout: %v", err)
}
time.Sleep(time.Nanosecond)
synctest.Wait()
if err := ctx.Err(); err != context.DeadlineExceeded {
t.Fatalf("after timeout: got %v, want DeadlineExceeded", err)
}
})
}
```
Use `synctest.Test` in Go 1.25+ and later. Do not use the old Go 1.24 experimental `synctest.Run` API in Go 1.25+ code. If a module explicitly targets Go 1.24 and opts into `GOEXPERIMENT=synctest`, use the old API only as a compatibility fallback.
Key differences in `synctest`:
- `time.Sleep` advances synthetic time instantly when the goroutine blocks
- `time.After` fires when synthetic time reaches the duration
- All goroutines run to blocking points before time advances
- Test execution is deterministic and repeatable
- Go 1.27+ adds `synctest.Sleep(d)` as a direct helper to advance the bubble's fake clock, equivalent to `time.Sleep(d)` followed by `synctest.Wait()` but without needing a real goroutine to block on
Go 1.27+ also adds `httptest.NewTestServer()`, an in-memory fake-network variant of `httptest.NewServer` that composes with `synctest` — no real socket, so server tests can run inside a `synctest.Test` bubble instead of needing `httptest.NewServer` plus real timers.
## Test Timeouts
For tests that may hang, use a timeout helper that panics with caller location. See [Helpers](./references/helpers.md).
## Benchmarks
Write benchmarks as sub-benchmarks (`b.Run` per variant) so each variant gets its own name in the output — that name is what comparison tooling diffs. For Go 1.24+, use `b.Loop()` rather than a `b.N` loop.
→ See [Benchmarks in a Test Suite](./references/benchmarks.md) for the code shape and size-parameterized examples.
→ See `samber/cc-skills-golang@golang-benchmark` skill for measurement methodology: `benchstat`, profiling from benchmarks, and CI regression detection.
## Go 1.26+: test artifacts
When a test, benchmark, or fuzz target needs to persist files for inspection, use `ArtifactDir()` instead of ad-hoc paths or repo-local output.
```go
func TestRenderGoldenArtifact(t *testing.T) {
dir := t.ArtifactDir()
out := filepath.Join(dir, "rendered.json")
if err := os.WriteFile(out, renderedBytes, 0o644); err != nil {
t.Fatal(err)
}
t.Logf("artifact written: %s", out)
}
```
Available on `*testing.T`, `*testing.B`, and `*testing.F` in Go 1.26+.
### Go 1.27+: `stdversion` runs automatically
`go test` now invokes the `stdversion` vet check by default, flagging any use of an API newer than the module's `go` directive. A CI failure from this check means either the `go` directive needs bumping or the code needs to stop using the newer API — it is not a check to silence.
## Parallel Tests
Use `t.Parallel()` to run tests concurrently:
```go
func TestParallelOperations(t *testing.T) {
tests := []struct {
name string
data []byte
}{
{"small data", make([]byte, 1024)},
{"medium data", make([]byte, 1024*1024)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
is := assert.New(t)
result := Process(tt.data)
is.NotNil(result)
})
}
}
```
## Fuzzing
Use fuzzing to find edge cases and bugs:
```go
func FuzzReverse(f *testing.F) {
f.Add("hello")
f.Add("")
f.Add("a")
f.Fuzz(func(t *testing.T, input string) {
reversed := Reverse(input)
doubleReversed := Reverse(reversed)
if input != doubleReversed {
t.Errorf("Reverse(Reverse(%q)) = %q, want %q", input, doubleReversed, input)
}
})
}
```
## Examples as Documentation
`ExampleXxx` functions are executable documentation: `go test` compares their stdout to the `// Output:` comment, so a drifting example fails the build instead of misleading readers.
→ See [Examples as Documentation](./references/examples.md) for naming rules, `Unordered output`, and placement.
## Code Coverage
Generate a profile with `go test -coverprofile=coverage.out ./...`, then read the uncovered lines with `go tool cover -html=coverage.out`. Coverage locates untested paths; it does not measure assertion quality, so treat a percentage as a gap finder rather than a target.
→ See [Code Coverage](./references/coverage.md) for coverage modes, `-coverpkg`, and reporting pitfalls.
## Integration Tests
Use build tags to separate integration tests from unit tests:
```go
//go:build integration
package mypackage
func TestDatabaseIntegration(t *testing.T) {
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
t.Fatal(err)
}
defer db.Close()
// Test real database operations
}
```
Run integration tests separately:
```bash
go test -tags=integration ./...
```
For Docker Compose fixtures, SQL schemas, and integration test suites, see [Integration Testing](./references/integration-testing.md).
## Mocking
Mock interfaces, not concrete types. Define interfaces where consumed, then create mock implementations.
For mock patterns, test fixtures, and time mocking, see [Mocking](./references/mocking.md).
## Enforce with Linters
Many test best practices are enforced automatically by linters: `thelper`, `paralleltest`, `testifylint`. See the `samber/cc-skills-golang@golang-lint` skill for configuration and usage.
## Cross-References
- → See `samber/cc-skills-golang@golang-stretchr-testify` skill for detailed testify API (assert, require, mock, suite)
- → See `samber/cc-skills-golang@golang-database` skill (testing.md) for database integration test patterns
- → See `samber/cc-skills-golang@golang-concurrency` skill for goroutine leak detection with goleak
- → See `samber/cc-skills-golang@golang-continuous-integration` skill for CI test configuration and GitHub Actions workflows
- → See `samber/cc-skills-golang@golang-lint` skill for testifylint and paralleltest configuration
- → See `samber/cc-skills-golang@golang-continuous-integration` skill for automated AI-driven code review in CI using these guidelines
## Quick Reference
```bash
go test ./... # all tests
go test -run TestName ./... # specific test by exact name
go test -run TestName/subtest ./... # subtests within a test
go test -run 'Test(Add|Sub)' ./... # multiple tests (regexp OR)
go test -run 'Test[A-Z]' ./... # tests starting with capital letter
go test -run 'TestUser.*' ./... # tests matching prefix
go test -run '.*Validation.*' ./... # tests containing substring
go test -run TestName/. ./... # all subtests of TestName
go test -run '/(unit|integration)' ./... # filter by subtest name
go test -race ./... # race detection
go test -cover ./... # coverage summary
go test -bench=. -benchmem ./... # benchmarks
go test -fuzz=FuzzName ./... # fuzzing
go test -tags=integration ./... # integration tests
```