用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tomevault-io/skills-registry --skill golang-testing命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | golang-testing |
| description | Write idiomatic Go tests. Use when this capability is needed. |
| metadata | {"author":"brpaz"} |
Use this skill when writing, reviewing, or refactoring Go tests.
Unless there is a clear reason not to, prefer these defaults:
package mypkg_testt.Parallel() by default in top-level tests and subtests unless shared state, process-wide mutation, timing, or external resources make it unsafet.Run subtests by default, even when not using table-driven teststestify/require for preconditions and fatal assertions, and testify/assert for non-fatal assertionstestify/mock only for true external-system boundaries or very small/simple seams; otherwise prefer fakes or in-memory implementationsPrefer:
package user_test
import (
"testing"
"github.com/stretchr/testify/require"
"example.com/project/user"
)
Avoid:
package user
Use same-package tests only when you intentionally need access to unexported helpers and there is no better public seam. Default to _test packages.
_test.go files named after the subject under test, such as service_test.go, handler_test.go, client_test.goFor a public function, prefer one top-level test and scenario subtests instead of many separate TestXxx_Yyy functions.
Use t.Run even without a table when the scenarios are clearer as explicitly written examples.
func TestCreateUser(t *testing.T) {
t.Parallel()
t.Run("success", func(t *testing.T) {
t.Parallel()
got, err := user.Create("alice@example.com")
require.NoError(t, err)
assert.Equal(t, "alice@example.com", got.Email)
})
t.Run("rejects invalid email", func(t *testing.T) {
t.Parallel()
_, err := user.Create("not-an-email")
require.Error(t, err)
require.ErrorIs(t, err, user.ErrInvalidEmail)
})
}
func TestParseUserID(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
want int
wantErr string
}{
{name: "valid", input: "42", want: 42},
{name: "empty", input: "", wantErr: "empty user id"},
{name: "invalid", input: "abc", wantErr: "invalid user id"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := user.ParseUserID(tt.input)
if tt.wantErr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.wantErr)
return
}
require.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}
go test -run and CI logst.Parallel()Call t.Parallel() unless there is a concrete reason not to.
Good candidates:
t.TempDir()httptest.ServerAvoid or carefully isolate parallelism when tests:
time.SleepIn Go 1.22+ modules, loop variables declared in the for statement are created per iteration, so rebinding like tt := tt is usually not needed.
Still rebind when:
For modern Go:
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
// use tt safely in Go 1.22+
})
}
For older module targets:
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
// use tt safely
})
}
Prefer Testify for clearer assertions.
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
require vs assertrequire when the test cannot continue after failureassert when you want multiple related checks in one scenariofunc TestUserName(t *testing.T) {
t.Parallel()
u, err := user.New("alice")
require.NoError(t, err)
assert.Equal(t, "alice", u.Name())
assert.True(t, u.IsActive())
}
Prefer semantic assertions when available:
require.ErrorIsassert.Equalassert.ElementsMatchassert.Lenassert.Empty / assert.NotEmptyassert.WithinDurationassert.JSONEqErrorIs / ErrorAsrequire.ErrorIs(t, err, user.ErrNotFound)
Aim for coverage that reflects risk and behaviour, not vanity percentages.
go test ./...
go test ./... -cover
go test ./... -coverprofile=coverage.out
go tool cover -func=coverage.out
go tool cover -html=coverage.out
Use the lightest test that proves the behaviour.
When an integration test gives stronger confidence with similar complexity, prefer it over elaborate mocking.
Mock only external systems or very thin boundaries. Prefer fakes and in-memory implementations for richer domain behaviour.
Prefer:
httptest.Serverfstest.MapFSReach for testify/mock when:
Avoid mocks for:
type MockMailer struct {
mock.Mock
}
func (m *MockMailer) Send(ctx context.Context, msg mail.Message) error {
args := m.Called(ctx, msg)
return args.Error(0)
}
func TestService_SendWelcomeEmail(t *testing.T) {
t.Parallel()
mailer := new(MockMailer)
mailer.
On("Send", mock.Anything, mail.Message{To: "alice@example.com"}).
Return(nil).
Once()
svc := user.NewService(mailer)
err := svc.SendWelcomeEmail(context.Background(), "alice@example.com")
require.NoError(t, err)
mailer.AssertExpectations(t)
}
Rules:
Prefer standard-library-friendly seams and hermetic storage.
fs.FS for read-only filesystem behaviourfstest.MapFS for small read scenariost.TempDir() for realistic file creation/update flowsfstest.MapFS Examplefunc TestLoadConfig(t *testing.T) {
t.Parallel()
files := fstest.MapFS{
"config.json": {Data: []byte(`{"env":"test"}`)},
}
cfg, err := config.Load(files, "config.json")
require.NoError(t, err)
assert.Equal(t, "test", cfg.Env)
}
t.TempDir() Examplefunc TestWriteReport(t *testing.T) {
t.Parallel()
dir := t.TempDir()
path := filepath.Join(dir, "report.txt")
err := report.Write(path, "hello")
require.NoError(t, err)
data, err := os.ReadFile(path)
require.NoError(t, err)
assert.Equal(t, "hello", string(data))
}
Avoid mocking os calls directly when a temp dir or fs.FS seam would be simpler.
Prefer real HTTP semantics without real network dependencies.
httptest.Server to test client behaviour against realistic responses*http.Client into your codehttp.RoundTripper fake can be enoughhttptest.Server Examplefunc TestClient_GetUser(t *testing.T) {
t.Parallel()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodGet, r.Method)
assert.Equal(t, "/users/42", r.URL.Path)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id":42,"name":"alice"}`))
}))
t.Cleanup(srv.Close)
client := api.NewClient(srv.URL, srv.Client())
got, err := client.GetUser(context.Background(), 42)
require.NoError(t, err)
assert.Equal(t, "alice", got.Name)
}
For narrow client logic, prefer a tiny fake over a heavyweight mock:
type roundTripFunc func(*http.Request) (*http.Response, error)
func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
return f(r)
}
Use this when you only need to simulate one request/response pair and httptest.Server would be unnecessary ceremony.
Do not depend on real time in tests.
now func() time.Timetime.Sleep as a synchronisation mechanismtype Service struct {
now func() time.Time
}
func NewService() *Service {
return &Service{now: time.Now}
}
func TestTokenExpired(t *testing.T) {
t.Parallel()
fixed := time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
svc := token.NewService(func() time.Time { return fixed })
assert.True(t, svc.IsExpired(fixed.Add(-time.Second)))
assert.False(t, svc.IsExpired(fixed.Add(time.Second)))
}
If production code currently calls time.Now() directly in many places, first introduce a small seam at the package boundary rather than mocking time per call site.
t.Run subtests first without a table when scenarios are clearer as explicitly written examplesGood fit for table-driven tests:
Poor fit for table-driven tests:
t.Run blockst.Helper()func newTestUser(t *testing.T, opts ...UserOption) user.User {
t.Helper()
u, err := user.New("alice", opts...)
require.NoError(t, err)
return u
}
Avoid these unless there is a strong reason:
t.Run scenarios are clearert.Parallel() without reasonWhen reviewing Go tests, check for:
_test package usage by defaultt.Parallel() in top-level tests and subtests unless unsafetestify/assert and testify/require used appropriatelytestify/mock limited to external boundaries or simple seamsSource: brpaz/agent-skills — distributed by TomeVault.