Skip to main content
Kodelyth ECC
Command

/go-test

Enforce TDD workflow for Go. Write table-driven tests first, then implement. Verify 80%+ coverage with go test -cover.

Run:/go-test

Go TDD Command

This command enforces test-driven development methodology for Go code using idiomatic Go testing patterns.

What This Command Does

  • Define Types/Interfaces: Scaffold function signatures first
  • Write Table-Driven Tests: Create comprehensive test cases (RED)
  • Run Tests: Verify tests fail for the right reason
  • Implement Code: Write minimal code to pass (GREEN)
  • Refactor: Improve while keeping tests green
  • 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

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

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

for _, tt := range tests {
    tt := tt // Capture
    t.Run(tt.name, func(t *testing.T) {
        t.Parallel()
        // test body
    })
}

Test Helpers

func setupTestDB(t *testing.T) *sql.DB {
    t.Helper()
    db := createDB()
    t.Cleanup(func() { db.Close() })
    return db
}

Coverage Commands

# 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/