Files
samber_cc-skills-golang/skills/golang-google-wire/references/testing.md
T
Samuel BertheandGitHub ec8c349e29 docs: comprehensive skill-writing guideline update and retroactive fixes (#100)
* docs: tighten skill description guidelines and fix overlap gaps

Description frontmatter is the only signal Claude Code reads before
deciding to load a skill, so under-specified triggers or missing
sibling boundaries directly cause mis-selection or silent skipping.

- add 8 description-writing rules to CLAUDE.md (ordering, point of
  view, concrete nouns, pushy-but-scoped triggers, sibling scoping,
  front-loading, no workflow narration, negative-clause siblings)
- add length-calibration guidance reserving long descriptions for
  moment-triggered skills, distinct from topic-triggered ones
- retroactively apply the new rules to the 16 skills that violated
  them: missing overlap disclaimers (golang-security, golang-safety,
  golang-concurrency, golang-troubleshooting, golang-dependency-management,
  golang-continuous-integration, golang-design-patterns, golang-modernize,
  golang-project-layout, golang-popular-libraries, golang-lint,
  golang-data-structures, golang-samber-mo), oversized descriptions
  (golang-refactoring, golang-pkg-go-dev), and a too-abstract one
  (golang-stay-updated)

* docs: add skill body writing style guidelines

Body content quality (voice, terminology, specificity, feedback
loops) was undocumented even though description quality already had
a dedicated section — leaving body-writing conventions to individual
judgment call by call.

- add a Body writing style section: imperative verb-first voice, one
  term per concept, one default with an escape hatch, assume reader
  competence, tables/checklists over prose, specificity matched to
  fragility, copyable progress checklists, feedback loops over rule
  enumeration
- fold the ALWAYS/NEVER-in-caps-as-a-smell refinement into the
  existing Teach reasoning, not only rules section
- add the under-250-lines target and 147-line official median to
  the existing Token budgets line-count bullet

* docs: apply new body writing style rules to 11 skill bodies

Retroactively applies CLAUDE.md's new Body writing style rules
(added earlier on this branch) after a read-only audit of all 46
active skill bodies flagged 10 real violations.

- add rationale to bare ALL-CAPS ALWAYS/NEVER items that weren't
  actually order-dependent or destructive (golang-context,
  golang-modernize, golang-samber-do, golang-project-layout,
  golang-structs-interfaces, golang-testing, golang-troubleshooting)
- split oversized bodies into references/: golang-structs-interfaces
  (386 -> 300 lines, new struct-fields.md and type-assertions.md)
  and golang-testing (476 -> 417 lines, new benchmarks.md,
  coverage.md, examples.md)
- trim golang-dependency-injection's duplicated 4-library comparison
  down to a 2-way contrast, pointing to its existing reference files
  for the rest
- merge golang-safety's two split Cross-References sections into one
- normalize the ASCII "->" arrow to "→" for consistency with the
  rest of the repo (golang-samber-mo, golang-testing,
  golang-concurrency)

* docs: add progressive disclosure guidance for skill bodies

The old Progressive disclosure section had a merge artifact (three
numbered items all labeled "Instructions" with three different,
overlapping token thresholds) and said nothing about why references
matter or how compaction and nested reads actually fail.

- reframe around the real asymmetry: body content is a recurring
  per-turn cost, references/ is paid once and only if loaded
- fix the duplicate "Instructions" bullets by cross-referencing
  Token budgets instead of restating three conflicting numbers
- document the nested-reference truncation failure mode (head -100
  silently drops the deepest content) on the existing one-level-deep
  rule in the Skill Body intro
- add the auto-compaction budget fact (~5,000 tokens survive per
  skill, ~25,000 shared) and the table-of-contents-over-100-lines,
  organize-by-domain, and explicit-load-pointer rules

* docs: add tables of contents to long reference files

CLAUDE.md's new progressive disclosure guidance requires a Table of
Contents on any reference file over 100 lines, so a partial read
(head -100, or truncation on a long file) still reveals the file's
full scope instead of silently hiding sections past the cutoff.

Generated mechanically for all 123 qualifying reference files (every
skills/*/references/*.md over 100 lines that didn't already have
one) with a one-off script: extract H2/H3 headings outside fenced
code blocks, build GitHub-compatible anchors, insert after the H1
title and intro paragraph. Idempotent - already-TOC'd files (the 4
golang-gopls reference files) were left untouched.

* docs: add bundling-scripts and security guidelines

Executable helpers and skill-level security had no dedicated home in
CLAUDE.md even though the plugin already ships scripts/ directories
and handles third-party library content that could carry injections.

- add a Bundling scripts section: when to bundle (deterministic,
  repeated, or fragile operations), errors handled inside the
  script, justified constants, forward-slash paths, explicit
  dependencies, execute-vs-read framing, and plan -> validate ->
  execute for batch or destructive work
- add a Security section anchored on the Principle of Lack of
  Surprise: no credential handling or data exfiltration, no runtime
  instruction-fetching, external content treated as data, allowed-
  tools granting without prompting even in untrusted directories,
  least-privilege tool scoping, and auditing bundled files (not just
  SKILL.md) before installing a third-party skill
- cross-reference existing Snyk agent scanner compliance and
  Library-specific skills sections instead of duplicating their
  detailed patterns

* docs: add anti-patterns quick-reference table

A single lookup table naming every failure mode and pointing at the
section that owns the fix makes today's accumulated guidance
(description, body, progressive disclosure, scripts, security)
scannable in one pass instead of requiring a full read to recall
where a given rule lives.

- add the Anti-patterns table before Evaluation, cross-referencing
  existing sections for rows already covered in full
- add net-new guidance where no existing rule covered the row:
  version-relative facts over date-relative ones with a collapsed
  <details> block for superseded patterns, forward slashes in body
  examples (not just scripts), a verified frontmatter field-count
  warning (confirmed against the Agent Skills spec: six fields -
  name, description, license, compatibility, metadata, allowed-tools
  - everything else is a harness extension), top-level version:
  rejection, description YAML-quoting pitfalls, MCP tool name
  server-qualification, allowed-tools as a pre-approval list rather
  than a sandbox, @-mention force-loading of another skill, a
  discovery-degradation ceiling around 20-50 installed skills, and
  per-model eval validity
- add a short "facts in CLAUDE.md, procedures in skills" principle
  to Project Overview, the CLAUDE.md-vs-skill counterpart to the
  existing skill-vs-skill Atomic skills and deduplication rule

* fix: correct TOC anchor slugs and prettier/markdownlint findings

Running the documented lint pipeline (prettier, then
markdownlint-cli2) surfaced a real bug in the TOC-generation script
used earlier on this branch: its anchor slugger collapsed runs of
whitespace into a single hyphen and stripped inline emphasis markers
without excluding intraword underscores, producing anchors that did
not match GitHub's actual algorithm (each whitespace character maps
to its own hyphen, with no collapsing). This broke 306 link
fragments across 61 reference files whose headings contained an
em dash, ampersand, parentheses, or a snake_case identifier.

- fix the slugify algorithm and regenerate all 123 previously
  generated TOCs; the fix is idempotent, so headings without special
  punctuation are byte-identical to before
- fix two `MD038` violations in CLAUDE.md (` : ` code spans with
  a trailing space) introduced by an earlier commit on this branch
- apply prettier's table-column alignment to two new reference files
  (golang-testing/references/coverage.md, examples.md)

20 anchors across two files (golang-how-to/references/by-category.md,
project-config.md) remain flagged by markdownlint's MD051 rule for
headings containing emoji with variation selectors -- its own --fix
computes the identical anchor its checker then rejects, a stable
fixed point that is a markdownlint-cli2 limitation, not a defect in
the generated content (the heading text itself is unaffected; only
the anchor's exact string fails the linter's cross-check). Left as a
known limitation rather than chased further.

All other lint findings across the repository (515 total, MD022/
MD037/MD025/etc.) are pre-existing and outside every file this
branch touches -- confirmed by diffing the error file list against
this branch's changed-file list.

* chore: bump patch version on all 40 skills changed on this branch

* chore: bump plugin version to 2.0.1

* oops
2026-09-01 11:45:24 +02:00

5.5 KiB

Testing — google/wire

Wire generates plain Go constructor calls, so tests work directly on the constructor layer — no container API to learn.

Table of Contents

Unit Tests: Plain Constructor Injection

The generated code has no wire dependency. Test constructors directly:

func TestUserService_GetUser(t *testing.T) {
    mockStore := &MockUserStore{users: map[int64]*User{1: &User{ID: 1, Name: "Alice"}}}
    cache := newTestRedis(t)
    svc := service.NewUserService(mockStore, cache)

    u, err := svc.GetUser(context.Background(), 1)
    require.NoError(t, err)
    assert.Equal(t, "Alice", u.Name)
}

Pass mocks directly as constructor arguments. No wire, no container, no file to generate. This is the idiomatic approach for unit tests.

Test Injectors: Swapping Providers

For integration or component tests where you want the full wired graph but with selected dependencies replaced, create a test-only injector in a _test.go file.

// app_test.go
//go:build wireinject

package main

import (
    "testing"
    "github.com/google/wire"
)

// TestSet replaces real infra with in-memory fakes
var TestSet = wire.NewSet(
    NewTestConfig,
    NewInMemoryUserStore,
    wire.Bind(new(repo.UserStore), new(*InMemoryUserStore)),
    NewTestRedis,
)

func InitTestApp(t *testing.T) (*App, func(), error) {
    wire.Build(TestSet, service.ServiceSet, NewApp)
    return nil, nil, nil
}
// app_integration_test.go
//go:build !wireinject  // compiles when the wireinject tag is NOT set

package main

func TestApp_GetUser(t *testing.T) {
    app, cleanup, err := InitTestApp(t)
    require.NoError(t, err)
    defer cleanup()

    // test against the fully-wired app with fake dependencies
    u, err := app.GetUser(context.Background(), 1)
    require.NoError(t, err)
    assert.NotNil(t, u)
}

Run wire ./... to generate wire_gen.go — the test injector is included because the _test.go file is compiled as part of the package during go test.

Key pattern from upstream best practices: Prefer creating a test-only provider set over passing mocks as injector arguments (though both work). The set approach keeps the test injector composable.

Passing Mocks as Injector Arguments

An alternative to a test set: pass the mock directly as an injector parameter. Wire treats it as a pre-built provider.

//go:build wireinject

func InitTestApp(store repo.UserStore) (*App, func(), error) {
    wire.Build(config.ConfigSet, service.ServiceSet, NewApp)
    return nil, nil, nil
}

// Test
func TestApp(t *testing.T) {
    mock := &MockUserStore{}
    app, cleanup, err := InitTestApp(mock)
    require.NoError(t, err)
    defer cleanup()
    // ...
}

Use this form when you only need to replace one or two dependencies and a full TestSet is overkill.

CI: Detecting Stale wire_gen.go

If wire_gen.go is not regenerated after a provider change, CI builds pass but the graph is wrong. Enforce freshness in CI:

# Option 1: re-run wire and check for diffs
wire ./...
git diff --exit-code -- '**/wire_gen.go'
# .github/workflows/ci.yml
- name: Check wire_gen.go is up-to-date
  run: |
    go install github.com/google/wire/cmd/wire@v0.7.0
    wire ./...
    git diff --exit-code -- '**/wire_gen.go'
# Option 2: use wire check (verifies graph without regenerating)
wire check ./...

wire check exits non-zero if the graph is inconsistent but does not update wire_gen.go. Use it for a fast graph-validity check without modifying files.

Testing Interface Bindings

wire.Bind can be used in test sets to bind a fake to the same interface:

// Fake implements the same interface as the real provider
type FakeMailer struct{ sent []string }
func (f *FakeMailer) Send(to, body string) error { f.sent = append(f.sent, to); return nil }

var TestMailerSet = wire.NewSet(
    NewFakeMailer,
    wire.Bind(new(notification.Mailer), new(*FakeMailer)),
)

var TestSet = wire.NewSet(
    TestMailerSet,
    realServiceSet,  // everything else is real
)

This keeps the test injector narrow — only the Mailer is faked; the rest of the graph is real.

Table-Driven Tests Without Wire

Wire is an initialization tool. Once the object graph is built, table-driven tests on individual services need no wire involvement:

func TestUserService(t *testing.T) {
    cases := []struct {
        name  string
        id    int64
        users map[int64]*User
        want  string
        err   bool
    }{
        {"found", 1, map[int64]*User{1: &User{Name: "Alice"}}, "Alice", false},
        {"not found", 99, nil, "", true},
    }
    for _, tc := range cases {
        t.Run(tc.name, func(t *testing.T) {
            svc := service.NewUserService(&MockUserStore{users: tc.users}, nil)
            u, err := svc.GetUser(context.Background(), tc.id)
            if tc.err { require.Error(t, err); return }
            assert.Equal(t, tc.want, u.Name)
        })
    }
}

Wire has no role here — the injector was only needed to build the object graph in main (or in an integration test). Unit tests construct dependencies directly.