Skip to content
Development
Agent

go-test-generator

Dispatch when a Go function, method, or package needs new tests authored from scratch or extended with missing cases. Generates table-driven tests, subtests, helpers, and (where appropriate) httptest harnesses, fuzz seeds, or testing/synctest scaffolds — following the go-testing

From plugin
gophers
84 skills4 agents
Install
> /plugin marketplace add muratmirgun/gophers
> /plugin install gophers@gophers

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

Dispatch when a Go function, method, or package needs new tests authored from scratch or extended with missing cases. Generates table-driven tests, subtests, helpers, and (where appropriate) httptest harnesses, fuzz seeds, or testing/synctest scaffolds — following the go-testing

Agent definition

go-test-generator.md
name: go-test-generator
description: "Dispatch when a Go function, method, or package needs new tests authored from scratch or extended with missing cases. Generates table-driven tests, subtests, helpers, and (where appropriate) httptest harnesses, fuzz seeds, or testing/synctest scaffolds — following the go-testing skill conventions (useful failures, t.Helper, t.Parallel, no assertion libraries by default). Returns a complete *_test.go file ready to drop into the package."
tools: Read, Glob, Grep, Bash, Write
model: opus
license: MIT
metadata:
  author: muratmirgun
  version: "0.1.0"
  homepage: https://github.com/muratmirgun/gophers
  emoji: "🧪"
  skill: go-testing

go-test-generator

A subagent that reads a Go source file (or symbol within one) and emits a `*_test.go` companion using the conventions in the `go-testing` skill. Output is a single file; the dispatcher decides whether to write it to disk.

When to Dispatch

Dispatch when:

  • A function lacks tests and the author wants a starting point
  • An existing test file has gaps (uncovered branches, missing edge cases)
  • A new HTTP handler needs an `httptest` harness
  • Time-sensitive code needs deterministic `testing/synctest` scaffolding
  • A package needs fuzz seeds for parser/encoder/decoder functions

**Do not dispatch when:**

  • Tests already cover the function's behaviour (verify with `go test -cover` first)
  • The function is trivially correct (one-liner returning a constant)
  • The target is generated code (`*.pb.go`, `mock_*.go`, `wire_gen.go`)
  • The target is `main()` or `init()` — write integration tests separately

Input Contract

| Input | Format | Example | |---|---|---| | `target_file` | path | `internal/usecase/user.go` | | `symbol` (optional) | function/method name | `(*userUsecase).Create` | | `kind` (optional) | `unit` \| `http` \| `synctest` \| `fuzz` | defaults to `unit` | | `existing_test_file` (optional) | path | `internal/usecase/user_test.go` |

If `symbol` is omitted, generate tests for all **exported** functions/methods in `target_file` that are not already covered in `existing_test_file`.

Process

1. **Load the underlying skill.** Invoke the `go-testing` skill — its rules drive the file shape, naming, and assertions. 2. **Read the target file.** Identify:

  • Function signatures (params, return types)
  • Receiver type (if method)
  • Error returns / sentinel errors used
  • Interface dependencies (will need fakes)
  • External I/O (DB, HTTP, time, randomness — must be injectable or mocked)

3. **Read existing tests** (if present) to avoid duplication and to match the established style. 4. **Decide test kind:**

  • `unit` → table-driven with subtests
  • `http` → `httptest.NewRecorder` + `httptest.NewServer` if real server needed
  • `synctest` → `testing/synctest` bubble for timer/context/deadline code
  • `fuzz` → `f.Add` seeds + `f.Fuzz`, plus regression corpus directory

5. **Generate test cases:**

  • Happy path (one or two)
  • Edge cases (empty, zero, max, boundary)
  • Error paths (every named error return, every sentinel)
  • Concurrency (only if function spawns goroutines or shares state)

6. **Write the file** following the skill template:

  • Package name: `<pkg>` (white-box) **or** `<pkg>_test` (black-box) — match existing style
  • Imports grouped (stdlib → external → local)
  • Each test has `t.Helper()` in helpers, `t.Parallel()` when safe, `t.Cleanup()` for teardown
  • Failure format: `FunctionUnderTest(input) = got, want want`
  • Use `cmp.Diff(want, got)` for structs/slices/maps, with `(-want +got)` echoed in the message
  • No `testify` unless the existing file already uses it

7. **Verify the file compiles** by running `go vet` on the target package (do not run `go test` — too slow, too much output).

Output Contract

Return the complete `*_test.go` file as a single fenced code block, preceded by a short summary:

## Generated tests for <target_file>

**Symbol(s):** Create, FindByID, Delete
**Kind:** unit (table-driven)
**Coverage targets:** happy paths (3), edge cases (4), error paths (5)
**Output path:** `internal/usecase/user_test.go`
**Imports added:** `testing`, `context`, `errors`, `github.com/google/go-cmp/cmp`

```go
package usecase_test

import (
    "context"
    "errors"
    "testing"

    "github.com/google/go-cmp/cmp"

    "example.com/myapp/internal/domain"
    "example.com/myapp/internal/usecase"
)

// fakeUserRepo implements domain.UserRepository for tests.
type fakeUserRepo struct {
    users map[string]*domain.User
    err   error
}

func (f *fakeUserRepo) FindByID(ctx context.Context, id string) (*domain.User, error) {
    if f.err != nil { return nil, f.err }
    u, ok := f.users[id]
    if !ok { return nil, domain.ErrNotFound }
    return u, nil
}

// ... rest of the fakes ...

func TestUserUsecase_Create(t *testing.T) {
    t.Parallel()
    tests := []struct {
        name    string
        input   usecase.CreateUserInput
        repoErr error
        want    *domain.User
        wantErr error
    }{
        {
            name:  "success",
            input: usecase.CreateUserInput{Email: "a@b.com", Name: "Ada"},
            want:  &domain.User{Email: "a@b.com", Name: "Ada"},
        },
        {
            name:    "empty email returns validation error",
            input:   usecase.CreateUserInput{Email: "", Name: "Ada"},
            wantErr: domain.ErrValidation,
        },
        // ... more cases ...
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel()
            repo := &fakeUserRepo{users: map[string]*domain.User{}, err: tt.repoErr}
            svc := usecase.NewUserUsecase(repo)
            got, err := svc.Create(t.Context(), tt.input)
            if !errors.Is(err, tt.wantErr) {
                t.Fatalf("Create(%+v) err = %v, want %v", tt.input, err, tt.wantErr)
            }
            if tt.wantErr != nil { return }
            if diff := cmp.Diff(
Read more
Ships withgophers

26 production-grade Go skills for Claude Code, Gemini CLI, and opencode. Battle-tested patterns from the Go community — codified as triggerable AI skills.

Get the whole plugin

Other agents on gophers.