KS
Killer-Skills

golang-testing — how to use golang-testing how to use golang-testing, golang testing patterns, TDD methodology in Go, golang-testing vs GoConvey, golang-testing install, golang testing best practices, table-driven tests in Go, subtests in golang-testing, golang-testing setup guide

Verified
v1.0.0
GitHub

About this Skill

Perfect for Code Development Agents requiring robust Go testing methodologies and TDD implementation. golang-testing is a set of Go testing patterns including table-driven tests, subtests, benchmarks, fuzzing, and test coverage, following TDD methodology with idiomatic Go practices.

Features

Table-driven tests for efficient test case management
Subtests for organizing and running related tests
Benchmarks for measuring performance-critical code
Fuzzing for input validation and error handling
Test coverage analysis for ensuring reliable code

# Core Topics

affaan-m affaan-m
[62.0k]
[7678]
Updated: 3/6/2026

Quality Score

Top 5%
80
Excellent
Based on code quality & docs
Installation
SYS Universal Install (Auto-Detect)
Cursor IDE Windsurf IDE VS Code IDE
> npx killer-skills add affaan-m/everything-claude-code/golang-testing

Agent Capability Analysis

The golang-testing MCP Server by affaan-m is an open-source Categories.official integration for Claude and other AI agents, enabling seamless task automation and capability expansion. Optimized for how to use golang-testing, golang testing patterns, TDD methodology in Go.

Ideal Agent Persona

Perfect for Code Development Agents requiring robust Go testing methodologies and TDD implementation.

Core Value

Enables agents to implement comprehensive Go testing patterns including table-driven tests, subtests, benchmarks, and fuzzing. Provides idiomatic Go practices for TDD workflow automation and test coverage analysis.

Capabilities Granted for golang-testing MCP Server

Implementing table-driven tests for multiple input scenarios
Running benchmarks for performance-critical code sections
Generating fuzz tests for input validation robustness
Creating subtests for hierarchical test organization
Analyzing test coverage metrics for code quality assurance

! Prerequisites & Limits

  • Go-specific testing framework only
  • Requires Go development environment
  • Limited to Go language test patterns
Project
SKILL.md
16.0 KB
.cursorrules
1.2 KB
package.json
240 B
Ready
UTF-8
SKILL.md
Readonly

Go Testing Patterns

Comprehensive Go testing patterns for writing reliable, maintainable tests following TDD methodology.

When to Activate

  • Writing new Go functions or methods
  • Adding test coverage to existing code
  • Creating benchmarks for performance-critical code
  • Implementing fuzz tests for input validation
  • Following TDD workflow in Go projects

TDD Workflow for Go

The RED-GREEN-REFACTOR Cycle

RED     → Write a failing test first
GREEN   → Write minimal code to pass the test
REFACTOR → Improve code while keeping tests green
REPEAT  → Continue with next requirement

Step-by-Step TDD in Go

go
1// Step 1: Define the interface/signature 2// calculator.go 3package calculator 4 5func Add(a, b int) int { 6 panic("not implemented") // Placeholder 7} 8 9// Step 2: Write failing test (RED) 10// calculator_test.go 11package calculator 12 13import "testing" 14 15func TestAdd(t *testing.T) { 16 got := Add(2, 3) 17 want := 5 18 if got != want { 19 t.Errorf("Add(2, 3) = %d; want %d", got, want) 20 } 21} 22 23// Step 3: Run test - verify FAIL 24// $ go test 25// --- FAIL: TestAdd (0.00s) 26// panic: not implemented 27 28// Step 4: Implement minimal code (GREEN) 29func Add(a, b int) int { 30 return a + b 31} 32 33// Step 5: Run test - verify PASS 34// $ go test 35// PASS 36 37// Step 6: Refactor if needed, verify tests still pass

Table-Driven Tests

The standard pattern for Go tests. Enables comprehensive coverage with minimal code.

go
1func TestAdd(t *testing.T) { 2 tests := []struct { 3 name string 4 a, b int 5 expected int 6 }{ 7 {"positive numbers", 2, 3, 5}, 8 {"negative numbers", -1, -2, -3}, 9 {"zero values", 0, 0, 0}, 10 {"mixed signs", -1, 1, 0}, 11 {"large numbers", 1000000, 2000000, 3000000}, 12 } 13 14 for _, tt := range tests { 15 t.Run(tt.name, func(t *testing.T) { 16 got := Add(tt.a, tt.b) 17 if got != tt.expected { 18 t.Errorf("Add(%d, %d) = %d; want %d", 19 tt.a, tt.b, got, tt.expected) 20 } 21 }) 22 } 23}

Table-Driven Tests with Error Cases

go
1func TestParseConfig(t *testing.T) { 2 tests := []struct { 3 name string 4 input string 5 want *Config 6 wantErr bool 7 }{ 8 { 9 name: "valid config", 10 input: `{"host": "localhost", "port": 8080}`, 11 want: &Config{Host: "localhost", Port: 8080}, 12 }, 13 { 14 name: "invalid JSON", 15 input: `{invalid}`, 16 wantErr: true, 17 }, 18 { 19 name: "empty input", 20 input: "", 21 wantErr: true, 22 }, 23 { 24 name: "minimal config", 25 input: `{}`, 26 want: &Config{}, // Zero value config 27 }, 28 } 29 30 for _, tt := range tests { 31 t.Run(tt.name, func(t *testing.T) { 32 got, err := ParseConfig(tt.input) 33 34 if tt.wantErr { 35 if err == nil { 36 t.Error("expected error, got nil") 37 } 38 return 39 } 40 41 if err != nil { 42 t.Fatalf("unexpected error: %v", err) 43 } 44 45 if !reflect.DeepEqual(got, tt.want) { 46 t.Errorf("got %+v; want %+v", got, tt.want) 47 } 48 }) 49 } 50}

Subtests and Sub-benchmarks

Organizing Related Tests

go
1func TestUser(t *testing.T) { 2 // Setup shared by all subtests 3 db := setupTestDB(t) 4 5 t.Run("Create", func(t *testing.T) { 6 user := &User{Name: "Alice"} 7 err := db.CreateUser(user) 8 if err != nil { 9 t.Fatalf("CreateUser failed: %v", err) 10 } 11 if user.ID == "" { 12 t.Error("expected user ID to be set") 13 } 14 }) 15 16 t.Run("Get", func(t *testing.T) { 17 user, err := db.GetUser("alice-id") 18 if err != nil { 19 t.Fatalf("GetUser failed: %v", err) 20 } 21 if user.Name != "Alice" { 22 t.Errorf("got name %q; want %q", user.Name, "Alice") 23 } 24 }) 25 26 t.Run("Update", func(t *testing.T) { 27 // ... 28 }) 29 30 t.Run("Delete", func(t *testing.T) { 31 // ... 32 }) 33}

Parallel Subtests

go
1func TestParallel(t *testing.T) { 2 tests := []struct { 3 name string 4 input string 5 }{ 6 {"case1", "input1"}, 7 {"case2", "input2"}, 8 {"case3", "input3"}, 9 } 10 11 for _, tt := range tests { 12 tt := tt // Capture range variable 13 t.Run(tt.name, func(t *testing.T) { 14 t.Parallel() // Run subtests in parallel 15 result := Process(tt.input) 16 // assertions... 17 _ = result 18 }) 19 } 20}

Test Helpers

Helper Functions

go
1func setupTestDB(t *testing.T) *sql.DB { 2 t.Helper() // Marks this as a helper function 3 4 db, err := sql.Open("sqlite3", ":memory:") 5 if err != nil { 6 t.Fatalf("failed to open database: %v", err) 7 } 8 9 // Cleanup when test finishes 10 t.Cleanup(func() { 11 db.Close() 12 }) 13 14 // Run migrations 15 if _, err := db.Exec(schema); err != nil { 16 t.Fatalf("failed to create schema: %v", err) 17 } 18 19 return db 20} 21 22func assertNoError(t *testing.T, err error) { 23 t.Helper() 24 if err != nil { 25 t.Fatalf("unexpected error: %v", err) 26 } 27} 28 29func assertEqual[T comparable](t *testing.T, got, want T) { 30 t.Helper() 31 if got != want { 32 t.Errorf("got %v; want %v", got, want) 33 } 34}

Temporary Files and Directories

go
1func TestFileProcessing(t *testing.T) { 2 // Create temp directory - automatically cleaned up 3 tmpDir := t.TempDir() 4 5 // Create test file 6 testFile := filepath.Join(tmpDir, "test.txt") 7 err := os.WriteFile(testFile, []byte("test content"), 0644) 8 if err != nil { 9 t.Fatalf("failed to create test file: %v", err) 10 } 11 12 // Run test 13 result, err := ProcessFile(testFile) 14 if err != nil { 15 t.Fatalf("ProcessFile failed: %v", err) 16 } 17 18 // Assert... 19 _ = result 20}

Golden Files

Testing against expected output files stored in testdata/.

go
1var update = flag.Bool("update", false, "update golden files") 2 3func TestRender(t *testing.T) { 4 tests := []struct { 5 name string 6 input Template 7 }{ 8 {"simple", Template{Name: "test"}}, 9 {"complex", Template{Name: "test", Items: []string{"a", "b"}}}, 10 } 11 12 for _, tt := range tests { 13 t.Run(tt.name, func(t *testing.T) { 14 got := Render(tt.input) 15 16 golden := filepath.Join("testdata", tt.name+".golden") 17 18 if *update { 19 // Update golden file: go test -update 20 err := os.WriteFile(golden, got, 0644) 21 if err != nil { 22 t.Fatalf("failed to update golden file: %v", err) 23 } 24 } 25 26 want, err := os.ReadFile(golden) 27 if err != nil { 28 t.Fatalf("failed to read golden file: %v", err) 29 } 30 31 if !bytes.Equal(got, want) { 32 t.Errorf("output mismatch:\ngot:\n%s\nwant:\n%s", got, want) 33 } 34 }) 35 } 36}

Mocking with Interfaces

Interface-Based Mocking

go
1// Define interface for dependencies 2type UserRepository interface { 3 GetUser(id string) (*User, error) 4 SaveUser(user *User) error 5} 6 7// Production implementation 8type PostgresUserRepository struct { 9 db *sql.DB 10} 11 12func (r *PostgresUserRepository) GetUser(id string) (*User, error) { 13 // Real database query 14} 15 16// Mock implementation for tests 17type MockUserRepository struct { 18 GetUserFunc func(id string) (*User, error) 19 SaveUserFunc func(user *User) error 20} 21 22func (m *MockUserRepository) GetUser(id string) (*User, error) { 23 return m.GetUserFunc(id) 24} 25 26func (m *MockUserRepository) SaveUser(user *User) error { 27 return m.SaveUserFunc(user) 28} 29 30// Test using mock 31func TestUserService(t *testing.T) { 32 mock := &MockUserRepository{ 33 GetUserFunc: func(id string) (*User, error) { 34 if id == "123" { 35 return &User{ID: "123", Name: "Alice"}, nil 36 } 37 return nil, ErrNotFound 38 }, 39 } 40 41 service := NewUserService(mock) 42 43 user, err := service.GetUserProfile("123") 44 if err != nil { 45 t.Fatalf("unexpected error: %v", err) 46 } 47 if user.Name != "Alice" { 48 t.Errorf("got name %q; want %q", user.Name, "Alice") 49 } 50}

Benchmarks

Basic Benchmarks

go
1func BenchmarkProcess(b *testing.B) { 2 data := generateTestData(1000) 3 b.ResetTimer() // Don't count setup time 4 5 for i := 0; i < b.N; i++ { 6 Process(data) 7 } 8} 9 10// Run: go test -bench=BenchmarkProcess -benchmem 11// Output: BenchmarkProcess-8 10000 105234 ns/op 4096 B/op 10 allocs/op

Benchmark with Different Sizes

go
1func BenchmarkSort(b *testing.B) { 2 sizes := []int{100, 1000, 10000, 100000} 3 4 for _, size := range sizes { 5 b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) { 6 data := generateRandomSlice(size) 7 b.ResetTimer() 8 9 for i := 0; i < b.N; i++ { 10 // Make a copy to avoid sorting already sorted data 11 tmp := make([]int, len(data)) 12 copy(tmp, data) 13 sort.Ints(tmp) 14 } 15 }) 16 } 17}

Memory Allocation Benchmarks

go
1func BenchmarkStringConcat(b *testing.B) { 2 parts := []string{"hello", "world", "foo", "bar", "baz"} 3 4 b.Run("plus", func(b *testing.B) { 5 for i := 0; i < b.N; i++ { 6 var s string 7 for _, p := range parts { 8 s += p 9 } 10 _ = s 11 } 12 }) 13 14 b.Run("builder", func(b *testing.B) { 15 for i := 0; i < b.N; i++ { 16 var sb strings.Builder 17 for _, p := range parts { 18 sb.WriteString(p) 19 } 20 _ = sb.String() 21 } 22 }) 23 24 b.Run("join", func(b *testing.B) { 25 for i := 0; i < b.N; i++ { 26 _ = strings.Join(parts, "") 27 } 28 }) 29}

Fuzzing (Go 1.18+)

Basic Fuzz Test

go
1func FuzzParseJSON(f *testing.F) { 2 // Add seed corpus 3 f.Add(`{"name": "test"}`) 4 f.Add(`{"count": 123}`) 5 f.Add(`[]`) 6 f.Add(`""`) 7 8 f.Fuzz(func(t *testing.T, input string) { 9 var result map[string]interface{} 10 err := json.Unmarshal([]byte(input), &result) 11 12 if err != nil { 13 // Invalid JSON is expected for random input 14 return 15 } 16 17 // If parsing succeeded, re-encoding should work 18 _, err = json.Marshal(result) 19 if err != nil { 20 t.Errorf("Marshal failed after successful Unmarshal: %v", err) 21 } 22 }) 23} 24 25// Run: go test -fuzz=FuzzParseJSON -fuzztime=30s

Fuzz Test with Multiple Inputs

go
1func FuzzCompare(f *testing.F) { 2 f.Add("hello", "world") 3 f.Add("", "") 4 f.Add("abc", "abc") 5 6 f.Fuzz(func(t *testing.T, a, b string) { 7 result := Compare(a, b) 8 9 // Property: Compare(a, a) should always equal 0 10 if a == b && result != 0 { 11 t.Errorf("Compare(%q, %q) = %d; want 0", a, b, result) 12 } 13 14 // Property: Compare(a, b) and Compare(b, a) should have opposite signs 15 reverse := Compare(b, a) 16 if (result > 0 && reverse >= 0) || (result < 0 && reverse <= 0) { 17 if result != 0 || reverse != 0 { 18 t.Errorf("Compare(%q, %q) = %d, Compare(%q, %q) = %d; inconsistent", 19 a, b, result, b, a, reverse) 20 } 21 } 22 }) 23}

Test Coverage

Running Coverage

bash
1# Basic coverage 2go test -cover ./... 3 4# Generate coverage profile 5go test -coverprofile=coverage.out ./... 6 7# View coverage in browser 8go tool cover -html=coverage.out 9 10# View coverage by function 11go tool cover -func=coverage.out 12 13# Coverage with race detection 14go test -race -coverprofile=coverage.out ./...

Coverage Targets

Code TypeTarget
Critical business logic100%
Public APIs90%+
General code80%+
Generated codeExclude

Excluding Generated Code from Coverage

go
1//go:generate mockgen -source=interface.go -destination=mock_interface.go 2 3// In coverage profile, exclude with build tags: 4// go test -cover -tags=!generate ./...

HTTP Handler Testing

go
1func TestHealthHandler(t *testing.T) { 2 // Create request 3 req := httptest.NewRequest(http.MethodGet, "/health", nil) 4 w := httptest.NewRecorder() 5 6 // Call handler 7 HealthHandler(w, req) 8 9 // Check response 10 resp := w.Result() 11 defer resp.Body.Close() 12 13 if resp.StatusCode != http.StatusOK { 14 t.Errorf("got status %d; want %d", resp.StatusCode, http.StatusOK) 15 } 16 17 body, _ := io.ReadAll(resp.Body) 18 if string(body) != "OK" { 19 t.Errorf("got body %q; want %q", body, "OK") 20 } 21} 22 23func TestAPIHandler(t *testing.T) { 24 tests := []struct { 25 name string 26 method string 27 path string 28 body string 29 wantStatus int 30 wantBody string 31 }{ 32 { 33 name: "get user", 34 method: http.MethodGet, 35 path: "/users/123", 36 wantStatus: http.StatusOK, 37 wantBody: `{"id":"123","name":"Alice"}`, 38 }, 39 { 40 name: "not found", 41 method: http.MethodGet, 42 path: "/users/999", 43 wantStatus: http.StatusNotFound, 44 }, 45 { 46 name: "create user", 47 method: http.MethodPost, 48 path: "/users", 49 body: `{"name":"Bob"}`, 50 wantStatus: http.StatusCreated, 51 }, 52 } 53 54 handler := NewAPIHandler() 55 56 for _, tt := range tests { 57 t.Run(tt.name, func(t *testing.T) { 58 var body io.Reader 59 if tt.body != "" { 60 body = strings.NewReader(tt.body) 61 } 62 63 req := httptest.NewRequest(tt.method, tt.path, body) 64 req.Header.Set("Content-Type", "application/json") 65 w := httptest.NewRecorder() 66 67 handler.ServeHTTP(w, req) 68 69 if w.Code != tt.wantStatus { 70 t.Errorf("got status %d; want %d", w.Code, tt.wantStatus) 71 } 72 73 if tt.wantBody != "" && w.Body.String() != tt.wantBody { 74 t.Errorf("got body %q; want %q", w.Body.String(), tt.wantBody) 75 } 76 }) 77 } 78}

Testing Commands

bash
1# Run all tests 2go test ./... 3 4# Run tests with verbose output 5go test -v ./... 6 7# Run specific test 8go test -run TestAdd ./... 9 10# Run tests matching pattern 11go test -run "TestUser/Create" ./... 12 13# Run tests with race detector 14go test -race ./... 15 16# Run tests with coverage 17go test -cover -coverprofile=coverage.out ./... 18 19# Run short tests only 20go test -short ./... 21 22# Run tests with timeout 23go test -timeout 30s ./... 24 25# Run benchmarks 26go test -bench=. -benchmem ./... 27 28# Run fuzzing 29go test -fuzz=FuzzParse -fuzztime=30s ./... 30 31# Count test runs (for flaky test detection) 32go test -count=10 ./...

Best Practices

DO:

  • Write tests FIRST (TDD)
  • Use table-driven tests for comprehensive coverage
  • Test behavior, not implementation
  • Use t.Helper() in helper functions
  • Use t.Parallel() for independent tests
  • Clean up resources with t.Cleanup()
  • Use meaningful test names that describe the scenario

DON'T:

  • Test private functions directly (test through public API)
  • Use time.Sleep() in tests (use channels or conditions)
  • Ignore flaky tests (fix or remove them)
  • Mock everything (prefer integration tests when possible)
  • Skip error path testing

Integration with CI/CD

yaml
1# GitHub Actions example 2test: 3 runs-on: ubuntu-latest 4 steps: 5 - uses: actions/checkout@v4 6 - uses: actions/setup-go@v5 7 with: 8 go-version: '1.22' 9 10 - name: Run tests 11 run: go test -race -coverprofile=coverage.out ./... 12 13 - name: Check coverage 14 run: | 15 go tool cover -func=coverage.out | grep total | awk '{print $3}' | \ 16 awk -F'%' '{if ($1 < 80) exit 1}'

Remember: Tests are documentation. They show how your code is meant to be used. Write them clearly and keep them up to date.

Related Skills

Looking for an alternative to golang-testing or building a Categories.official AI Agent? Explore these related open-source MCP Servers.

View All

flags

Logo of facebook
facebook

flags is a feature flag management system that enables developers to check flag states, compare channels, and debug feature behavior differences across release channels.

243.6k
0
Design

extract-errors

Logo of facebook
facebook

extract-errors is a skill that assists in extracting and managing error codes in React applications using yarn extract-errors command.

243.6k
0
Design

fix

Logo of facebook
facebook

fix is a technical skill that resolves lint errors, formatting issues, and ensures code quality in declarative, frontend, and UI projects

243.6k
0
Design

flow

Logo of facebook
facebook

Flow is a type checking system for JavaScript, used to validate React code and ensure consistency across applications

243.6k
0
Design