go-testing-with-testify — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited go-testing-with-testify (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Workflow for writing, reviewing, and hardening Go tests built on the standard testing package and the stretchr/testify toolkit (assert, require, mock, suite).
Go+testify tests are checks — automated pass/fail assertions on behavior contracts. Testing as investigation — charters, heuristics, oracles under stress, edge-case discovery, perspective rotation — is upstream. Compose with tester-mindset when strategy, risk framing, or the shape of the unknown dominates.
*_test.go files: tightening assertions, extractingtable-driven tests, introducing subtests, or adding t.Parallel()
-race complaints, order-dependent)reflect.DeepEqual / string-match / manual error checking withtestify assertions, or deciding when not to
testify/mock, or between flatsubtests and a suite.Suite
tester-mindset for claims, oracles, and edge-case coveragebackend-guidance or backend-systems-guidance for service-boundary test design in Go services
one-off install step (go mod init, go get github.com/stretchr/testify) not a skill-shaped workflow
testing code in the loop — that is atester-mindset job
primary; use the repo's implementation or review skill set instead, typically backend-guidance or backend-systems-guidance for Go services
go-cmp-only, or gocheck suites — the testify idioms belowwill fight those stacks
digraph route {
"Main artifact is testify-based Go test code or test review?" [shape=diamond];
"Main artifact is strategy,\nedge-case discovery, or test planning\nwithout concrete test code?" [shape=diamond];
"go-testing-with-testify" [shape=box];
"tester-mindset" [shape=box];
"coding-guidance-go or another repo skill set" [shape=box];
"Main artifact is testify-based Go test code or test review?" -> "go-testing-with-testify" [label="yes"];
"Main artifact is testify-based Go test code or test review?" -> "Main artifact is strategy,\nedge-case discovery, or test planning\nwithout concrete test code?" [label="no"];
"Main artifact is strategy,\nedge-case discovery, or test planning\nwithout concrete test code?" -> "tester-mindset" [label="yes"];
"Main artifact is strategy,\nedge-case discovery, or test planning\nwithout concrete test code?" -> "coding-guidance-go or another repo skill set" [label="no"];
}tester-mindset for claims, oracles, consequenceframing, and stopping rules
coding-guidance-go for production Go code, packagedesign, errors, context, concurrency, and review
backend-guidance (baseline) orbackend-systems-guidance (non-trivial boundaries, repositories, queues) for what to test at which seam
security and, for auth/session/tenant work,security-identity-access
project-config-and-tests forconfig contracts, path helpers, and deterministic data
documenter for repo-levelTESTING.md or contributor guides
Many Go testing tasks are just this skill plus tester-mindset when the claim or edge-case framing still needs work. Add a backend overlay only when the seam is actually a handler, repository, queue, or cross-service boundary. For non-test Go implementation or review, start with coding-guidance-go and add this skill only when testify-based tests are the main artifact.
assert vs require, equality variants, error-wrap assertions, async readiness helpers
hand-written fakes, testify/mock, ownership rules, argument matching, optional calls, and order constraints
— cheapest-honest-boundary defaults for HTTP, repository, filesystem, clocks, and async seams before reaching for mocks
suite.Suite tradeoffs, subtests, t.Parallel(), t.Setenv, t.TempDir, t.Cleanup, t.Context, deterministic ordering
maintainer-only RED/GREEN scenarios
— maintainer-only doc coverage audit and routing checks
Load reference files on demand; do not read the entire folder up front.
*_test.go, name thebehavior claim in one falsifiable sentence, the relevant stakeholders, and the cost of being wrong. Pick the seam: pure function, package API, HTTP handler, repository, or cross-service integration. The seam decides whether a table-driven unit test, a httptest.Server-backed integration test, or a -tags=integration test against a real dependency is the right consequence to invite.
touching a DB, queue, HTTP server, filesystem, or clock → use the real thing when it is cheap (testcontainers-go, httptest.NewServer, t.TempDir(), context.WithCancel) before reaching for a mock. Mocks belong at owned interface seams you control, not at the SUT. See references/real-boundary-patterns.md for concrete harness shapes.
string, run it via t.Run(tc.name, func(t *testing.T) { … }), and use t.Parallel() deliberately — not reflexively. See references/suite-and-parallelism.md for the parallelism checklist.
require.* haltsthe test on failure; use it for preconditions whose failure would make the rest of the test meaningless (setup, err from the action under test when subsequent asserts read the return value). assert.* continues; use it for independent output checks so one failing row does not hide the others.
assert.ErrorIs(t, err, ErrNotFound) over assert.Error plus string matching; assert.ElementsMatch over sorting-then-Equal when order is incidental; assert.JSONEq over byte-for-byte string equality on JSON. See references/assertion-patterns.md for the full menu.
HTTP client, and environment are the usual flake sources. If a test needs time.Now() to land in a specific window, the code has a design tap — inject a Clock interface or pass the timestamp as a parameter. Hard-to-test behavior is design feedback; refactor the seam before wrapping it in sleeps or retries.
go test -race -count=10 ./… on changed packages; more when the test touches concurrency. A single green run proves almost nothing about parallel tests.
inputs, which failure modes, which environments, which timings were left out. This becomes the input to the next consequence — integration test, fuzz target, property test, chaos probe, or monitoring — if risk remains.
Before rewriting or approving testify-based tests:
*_test.go files inscope. Prefer narrow package targets over ./... while iterating.
go.mod and the existing neighboring tests first. The module's Goversion changes what loop-variable capture and testing.T helpers are available.
plain assert/require, mock, suite, assert.New(t), or a local helper layer.
go test ./path/to/pkg -run '^TestName$' -count=1, then harden later with -race, higher -count, or -shuffle=on as the claim requires.
golangci-lint with testifylint, preserve orsatisfy it. Testify's own README recommends testifylint to avoid common mistakes.
The testify API is almost entirely a checking toolkit: automated pass/fail rules on known expectations. That is valuable and not in tension with tester-mindset — unless agents mistake passing checks for having tested. Before writing or approving a test, name the claim, the oracle, and what the test cannot disprove. Passing assertions reduce uncertainty; they do not convert partial coverage into certainty.
Heuristics, not laws. Treat them as fallible lenses that fit most cases; name the context when you break them.
assert.Equal(t, expected, actual). Swapping flipsfailure messages and trains readers to distrust the diff. Same for NotEqual, ElementsMatch, InDelta.
assert.ErrorIs / assert.ErrorAs for sentinel andtyped-error checks. Reserve assert.EqualError / string matching for messages that are part of the public contract (CLI output, API error body). Never assert on wrapped error text that the stdlib or a dependency can reword.
success path is a check that the code compiles under ideal input.
stronger assertion exists. require.NoError(t, err); assert.Equal(t, want, got.Field) beats assert.NotNil(t, got).
source: a struct literal with six fields, test asserts on all six via Equal, an unrelated field is added, every test breaks. Compare only the fields whose behavior the test claims.
the helper line, not the test line. Non-negotiable for shared setup and assertion wrappers.
assert.Eventually /require.Eventually with a tight tick, an injected clock, or a channel. Never time.Sleep in a test outside of an explicit reason documented inline.
stays on the test goroutine.** require` terminates the currenttest via t.FailNow(). Do not call it from helper goroutines spawned by the test.
hand-written struct that implements the interface is clearer than a mock setup block, and it survives refactors better because the compiler checks it.
assertions, or when the test genuinely needs to verify the arguments a collaborator was called with. Always mock.AssertExpectations(t) at the end so missing calls fail the test.
and fake that. Direct mocks of third-party types are brittle and rot on every dependency bump.
the type it is testing, the test is asserting on the mock, not the code. Split the type or flip the dependency.
speaks HTTP, a real server with handlers the test controls is usually the cheapest real boundary.
See references/mocking-patterns.md for the full rules and concrete examples.
t.Run(tc.name, …) is the default. One test, one row'sconcern. Named rows mean the -run TestFoo/specific_case flag works for local triage.
t.Parallel() is a claim that the test shares no hidden state with itssiblings. Before adding it, check for: package-level globals, shared temp dirs, a shared DB schema, os.Setenv, and time-of-day assumptions.
by default, so tc := tc is no longer required. On older modules, keep the rebind. Either way, prefer the explicit rebind in shared review templates — it is invariant across versions.
suite.Suite is familiar to xUnit refugees but discourages parallelism(the shared receiver is not safe for t.Parallel()) and hides setup/state behind SetupTest/TearDownTest. Use it for a coherent scenario group that truly shares setup cost; reach for flat subtests otherwise. See references/suite-and-parallelism.md.
When a test is flaky, treat the flake as a signal, not a nuisance.
go test -race -count=100 -run TestFoo ./pkg/.... Add-parallel=4 or -parallel=1 to see if concurrency is the vector.
iteration order, a goroutine scheduling order, or a shared temp dir? Those are the usual offenders.
-race complaint is a real bug, not testflake. Fix the code or the test, never suppress.
assert.Eventually, an injectedclock, a synchronization primitive (sync.WaitGroup, channel), or a context.WithTimeout that the test can cancel.
go test -shuffle=on fails, the test relies onrun order. Fix the shared state, don't freeze the order.
t.Skip with a linked issue and adate-bounded expiry is acceptable for a failure the team cannot fix this sprint; t.Skip without either is hiding a bug.
Before blaming the production code, rule out the usual test-apparatus bugs:
-race failures: a real data race in the code or the testt.Setenv, current working directory, or packageglobals used under t.Parallel()
t.TempDir() and unique listenerstime.Now(), rand, UUID generation, or goroutinescheduling used as if deterministic
rebinding in parallel table tests; newer ones may expose stale review lore
-count=1 during rapiddiagnosis, then broaden again for stability checks
These are apparatus bugs, not proof the behavior contract is wrong.
Stop adding tests when the claim has risk-appropriate evidence, remaining uncertainty is named, and the next test would cost more than the confidence it could add. If material risk remains but more unit tests are inefficient, shift to integration tests, fuzzing, property tests, load probes, monitoring, or staged rollout. Coverage percent is a weak oracle; the tester-mindset stopping rule beats a number.
Reject tests that cannot meaningfully fail for the right reason.
Forbidden patterns:
assert.True(t, true), assert.Equal(t, x, x), and any assertion whoseoperands are both computed from the same source.
assert.NotNil(t, resp); assert.NoError(t, err)with no check on the value the function returns.
require.NoError(t, err) on the action under test andthen assert nothing else. The function ran; that is not a behavior claim.
stored state exists. Logs are a side channel, not a contract.
assert.Contains(t, err.Error(), "not found") when `errors.Is(err,ErrNotFound)` would work. String-match on errors that are not part of the public contract is a brittleness trap.
roundtrip through a mock that the test itself configured proves the mock, not the code.
t.Skip("flaky") without an issue link and an expiry.Self-verify after writing: if I corrupted the production behavior in the simplest plausible way, would this test fail? If not, the test is proof theater.
Pressure excuses belong in references/pressure-tests.md. Use that reference when revising this skill, reviewing a contentious test change, or checking whether a proposed shortcut weakens evidence.
When recommending or delivering tests, include:
stakeholders + cost, seam = package API, table-driven subtests for validation failure modes, one httptest.NewServer integration for the mailer boundary, ErrorIs on sentinel errors; pair with backend-guidance for handler-side wiring.
rows for malformed input, normalization edge cases, and wrapped parse errors; assert.ErrorIs for sentinels, no backend overlay needed.
-race -count=100, inspect fixture (temp dirs, env, timezone), check -shuffle=on, look at -parallel; propose injected clock or Eventually if the failure is timing-bound.
setup is cheap enough to recreate per row; migrate to table-driven with a newTestEnv(t) helper; confirm each row can t.Parallel() after an explicit shared-state audit.
seam, check if the interface is owned and narrow (fake) vs wide or third-party (mock-with-own-wrapper); for HTTP, prefer httptest.NewServer.
tester-mindsetfirst for charters and heuristics; return here to encode them as assert.JSONEq rows.
body.
loop-variable semantics in 1.22, new testing.TB helpers, testify assertions), update the affected reference and add a pressure-test row if it closes a rationalization.
references/pressure-tests.md are the verificationharness for this skill. Run a representative subset before any substantive rewrite.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.