Skip to content
Back to skills

Go Testing

ASecurity

Go testing with go test, testify, and gomock. Covers unit tests, table-driven tests, benchmarks, mocking, and test coverage. USE WHEN: user mentions "go test", "golang test", "testify", asks about "table-driven test", "gomock", "go benchmark", "testing.T", "httptest" DO NOT USE FOR: JavaScript/TypeScript - use `vitest` or `jest`; Java - use `junit`; Python - use `pytest`; E2E browser tests - use Playwright; Rust - use `rust-testing`

  • 31 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 8, 2026
ai-agentsjavascripttypescriptpythonrustgojavabashsqltestinggit

Works with

  • api
  • mcp

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 8, 2026

npx -y skills add claude-dev-suite/claude-dev-suite --skill go-testing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Go Testing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Go Testing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/claude-dev-suite-go-testing/badge)](https://www.skillsdirectory.com/skills/claude-dev-suite-go-testing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: go-testing
description: |
  Go testing with go test, testify, and gomock. Covers unit tests, table-driven
  tests, benchmarks, mocking, and test coverage.

  USE WHEN: user mentions "go test", "golang test", "testify", asks about "table-driven test", "gomock", "go benchmark", "testing.T", "httptest"

  DO NOT USE FOR: JavaScript/TypeScript - use `vitest` or `jest`; Java - use `junit`; Python - use `pytest`; E2E browser tests - use Playwright; Rust - use `rust-testing`
allowed-tools: Read, Grep, Glob, Write, Edit, Bash
---

# Go Testing Core Knowledge

> **Deep Knowledge**: Use `mcp__documentation__fetch_docs` with technology: `go` for comprehensive documentation.

> **Full Reference**: See [advanced.md](advanced.md) for HTTP Testing, Benchmarks, Test Helpers, Test Fixtures, Integration Tests, Parallel Tests, and Test Coverage.

## When NOT to Use This Skill

- **JavaScript/TypeScript Projects** - Use `vitest` or `jest`
- **Java Projects** - Use `junit` for Java testing
- **Python Projects** - Use `pytest` for Python
- **E2E Browser Testing** - Use Playwright or Selenium
- **Rust Projects** - Use `rust-testing` skill

## Basic Testing

### Unit Tests

```go
// math.go
package math

func Add(a, b int) int {
    return a + b
}

func Divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

// math_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    if result != 5 {
        t.Errorf("Add(2, 3) = %d; want 5", result)
    }
}

func TestDivide(t *testing.T) {
    result, err := Divide(10, 2)
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    if result != 5.0 {
        t.Errorf("Divide(10, 2) = %f; want 5.0", result)
    }
}

func TestDivideByZero(t *testing.T) {
    _, err := Divide(10, 0)
    if err == nil {
        t.Error("expected error for division by zero")
    }
}
```

### Running Tests

```bash
# Run all tests
go test ./...

# Run tests in current package
go test

# Run specific test
go test -run TestAdd

# Verbose output
go test -v

# Run with coverage
go test -cover

# Generate coverage report
go test -coverprofile=coverage.out
go tool cover -html=coverage.out

# Run tests with race detector
go test -race

# Set timeout
go test -timeout 30s
```

## Table-Driven Tests

```go
func TestAddTableDriven(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive numbers", 2, 3, 5},
        {"negative numbers", -1, -1, -2},
        {"zero", 0, 0, 0},
        {"mixed", -1, 1, 0},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d; want %d",
                    tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

func TestDivideTableDriven(t *testing.T) {
    tests := []struct {
        name      string
        a, b      float64
        expected  float64
        expectErr bool
    }{
        {"normal division", 10, 2, 5, false},
        {"division by zero", 10, 0, 0, true},
        {"negative result", -10, 2, -5, false},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result, err := Divide(tt.a, tt.b)

            if tt.expectErr {
                if err == nil {
                    t.Error("expected error, got nil")
                }
                return
            }

            if err != nil {
                t.Fatalf("unexpected error: %v", err)
            }
            if result != tt.expected {
                t.Errorf("got %f; want %f", result, tt.expected)
            }
        })
    }
}
```

## Testify

```bash
go get github.com/stretchr/testify
```

### Assert Package

```go
import (
    "testing"
    "github.com/stretchr/testify/assert"
)

func TestWithAssert(t *testing.T) {
    // Equality
    assert.Equal(t, 5, Add(2, 3))
    assert.NotEqual(t, 6, Add(2, 3))

    // Boolean
    assert.True(t, true)
    assert.False(t, false)

    // Nil checks
    assert.Nil(t, nil)
    assert.NotNil(t, "value")

    // Error checks
    _, err := Divide(10, 0)
    assert.Error(t, err)
    assert.EqualError(t, err, "division by zero")

    result, err := Divide(10, 2)
    assert.NoError(t, err)
    assert.Equal(t, 5.0, result)

    // Contains
    assert.Contains(t, "hello world", "world")
    assert.Contains(t, []int{1, 2, 3}, 2)

    // Length
    assert.Len(t, []int{1, 2, 3}, 3)

    // Panics
    assert.Panics(t, func() { panic("boom") })
}
```

### Require Package

```go
import (
    "testing"
    "github.com/stretchr/testify/require"
)

func TestWithRequire(t *testing.T) {
    // require stops test on failure (unlike assert)
    result, err := Divide(10, 2)
    require.NoError(t, err) // Test stops here if err != nil
    require.Equal(t, 5.0, result)
}
```

### Suite Package

```go
import (
    "testing"
    "github.com/stretchr/testify/suite"
)

type UserServiceTestSuite struct {
    suite.Suite
    db      *sql.DB
    service *UserService
}

func (s *UserServiceTestSuite) SetupSuite() {
    // Run once before all tests
    var err error
    s.db, err = sql.Open("postgres", "test-connection")
    s.Require().NoError(err)
}

func (s *UserServiceTestSuite) TearDownSuite() {
    // Run once after all tests
    s.db.Close()
}

func (s *UserServiceTestSuite) SetupTest() {
    // Run before each test
    s.service = NewUserService(s.db)
}

func (s *UserServiceTestSuite) TearDownTest() {
    // Run after each test
    s.db.Exec("DELETE FROM users")
}

func (s *UserServiceTestSuite) TestCreateUser() {
    user, err := s.service.Create("alice", "alice@example.com")
    s.Require().NoError(err)
    s.Assert().Equal("alice", user.Name)
    s.Assert().NotZero(user.ID)
}

func TestUserServiceSuite(t *testing.T) {
    suite.Run(t, new(UserServiceTestSuite))
}
```

## Mocking with gomock

```bash
go install go.uber.org/mock/mockgen@latest
```

### Generate Mocks

```go
// user_repository.go
//go:generate mockgen -source=user_repository.go -destination=mocks/user_repository_mock.go -package=mocks

type UserRepository interface {
    Get(id string) (*User, error)
    Create(user *User) error
    Update(user *User) error
    Delete(id string) error
}
```

```bash
# Generate mocks
go generate ./...
```

### Using Mocks

```go
import (
    "testing"
    "github.com/stretchr/testify/assert"
    "go.uber.org/mock/gomock"
    "myapp/mocks"
)

func TestUserService_GetUser(t *testing.T) {
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()

    mockRepo := mocks.NewMockUserRepository(ctrl)

    // Set expectations
    expectedUser := &User{ID: "1", Name: "Alice"}
    mockRepo.EXPECT().
        Get("1").
        Return(expectedUser, nil).
        Times(1)

    // Create service with mock
    service := NewUserService(mockRepo)

    // Test
    user, err := service.GetUser("1")
    assert.NoError(t, err)
    assert.Equal(t, "Alice", user.Name)
}

func TestUserService_CreateUser(t *testing.T) {
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()

    mockRepo := mocks.NewMockUserRepository(ctrl)

    // Match any user with specific name
    mockRepo.EXPECT().
        Create(gomock.Any()).
        DoAndReturn(func(u *User) error {
            u.ID = "generated-id"
            return nil
        }).
        Times(1)

    service := NewUserService(mockRepo)
    user, err := service.CreateUser("Bob", "bob@example.com")

    assert.NoError(t, err)
    assert.Equal(t, "generated-id", user.ID)
}
```

### gomock Matchers

```go
func TestWithMatchers(t *testing.T) {
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()

    mock := mocks.NewMockUserRepository(ctrl)

    // Any value
    mock.EXPECT().Get(gomock.Any()).Return(nil, nil)

    // Specific value
    mock.EXPECT().Get(gomock.Eq("123")).Return(nil, nil)

    // Not equal
    mock.EXPECT().Get(gomock.Not("")).Return(nil, nil)

    // Custom matcher
    userWithName := gomock.Cond(func(u interface{}) bool {
        user, ok := u.(*User)
        return ok && user.Name != ""
    })
    mock.EXPECT().Create(userWithName).Return(nil)
}
```

### Call Order

```go
func TestCallOrder(t *testing.T) {
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()

    mock := mocks.NewMockUserRepository(ctrl)

    // InOrder ensures calls happen in sequence
    gomock.InOrder(
        mock.EXPECT().Get("1").Return(&User{ID: "1"}, nil),
        mock.EXPECT().Update(gomock.Any()).Return(nil),
    )

    // Use mock...
}
```

## Checklist

- [ ] Unit tests for all public functions
- [ ] Table-driven tests for multiple cases
- [ ] testify/assert for cleaner assertions
- [ ] Mock external dependencies
- [ ] HTTP handler tests
- [ ] Benchmarks for performance-critical code
- [ ] Integration tests with build tags
- [ ] Parallel tests where appropriate
- [ ] Coverage reporting in CI

## Anti-Patterns

| Anti-Pattern | Why It's Bad | Solution |
|--------------|--------------|----------|
| Not using table-driven tests | Duplicate test code | Use []struct{} for test cases |
| Testing unexported functions | Coupled to implementation | Test through exported API |
| Not using t.Helper() | Confusing error line numbers | Mark helper functions with t.Helper() |
| Sharing test state | Flaky, order-dependent | Isolate setup in each test |
| Not using subtests | All cases fail if one fails | Use t.Run() for subtests |
| Ignoring race detector | Hidden concurrency bugs | Run tests with -race flag |
| Not benchmarking | Performance regressions | Add benchmarks for critical paths |

## Quick Troubleshooting

| Problem | Likely Cause | Solution |
|---------|--------------|----------|
| "No tests found" | Wrong file naming | Use *_test.go naming |
| Race condition detected | Shared mutable state | Fix data races, use sync primitives |
| Mock not called | Wrong method or args | Check gomock expectations |
| Coverage not accurate | Missing test files | Run go test ./... for all packages |
| Test timeout | Infinite loop or slow operation | Add -timeout flag, investigate |
| "interface conversion" panic | Wrong mock interface | Ensure mock implements correct interface |

## Reference Documentation

- [Unit Tests](quick-ref/unit-tests.md)
- [Testify](quick-ref/testify.md)
- [Mocking](quick-ref/mocking.md)
- [Advanced Patterns](advanced.md)

Files in this skill

  • SKILL.md10.2 KB
  • advanced.md5.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.