Golang Stretchr Testify
Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured approach to using the stretchr/testify library in Go projects. It covers the core packages: assert, require, mock, and suite. It emphasizes best practices such as using require for preconditions and assert for verifications, maintaining correct argument order, and properly managing mock expectations. The guide includes patterns for async testing, custom matchers, and lifecycle management for test suites.
What it does
- Provides patterns for readable assertions using assert and require.
- Explains how to implement and verify mocks using testify/mock.
- Demonstrates how to structure tests with shared setup and teardown using testify/suite.
- Covers advanced testing techniques like async polling and JSON equality checks.
- Highlights common pitfalls such as incorrect argument order and missing mock verification.
- Offers guidance on choosing between assert and require based on failure behavior.
When to use it
- When writing unit or integration tests in a Go project that uses stretchr/testify.
- When you need to create mock implementations for interfaces to isolate units under test.
- When organizing related tests that require shared setup or teardown logic.
- When auditing existing test code to improve readability and reliability.
- When you need to perform complex assertions like unordered slice comparisons or async polling.
Advantages
- Improves test readability and maintainability compared to standard library testing.
- Reduces boilerplate code for common assertion and mocking tasks.
- Provides clear failure messages that simplify debugging.
- Enables robust testing of asynchronous operations and complex data structures.
- Standardizes test patterns across a codebase.
Usage examples
- “How do I use require vs assert in a Go test?”
- The agent explains that require stops execution immediately upon failure (ideal for preconditions), while assert records the failure and continues (ideal for general verifications).
- “Show me how to mock an interface with testify.”
- The agent provides a code example showing how to embed mock.Mock, implement methods using m.Called(), and verify expectations with AssertExpectations(t).
- “How can I test a function that eventually returns a specific status?”
- The agent demonstrates the use of is.Eventually or is.EventuallyWithT to poll for a condition within a specified timeout.
- “Help me set up a test suite for my service.”
- The agent provides a template for a struct-based test suite using suite.Suite, including SetupTest and a required TestXxx launcher function.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/samber/golang-stretchr-testify/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip golang-stretchr-testify.zip -d .agents/skills
Documentation by samber
Persona: You are a Go engineer who treats tests as executable specifications. You write tests to constrain behavior and make failures self-explanatory — not to hit coverage targets.
Modes:
Write mode — adding new tests or mocks to a codebase.
Review mode — auditing existing test code for testify misuse.
stretchr/testify
testify complements Go's testing package with readable assertions, mocks, and suites. It does not replace testing — always use *testing.T as the entry point.
This skill is not exhaustive — refer to library documentation and code examples for more information:
For Go package docs, symbols, versions, importers, and known vulnerabilities, → See
samber/cc-skills-golang@golang-pkg-go-devskill (godig), preferred over Context7 for Go package facts.To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See
samber/cc-skills-golang@golang-goplsskill (gopls).Context7 remains a fallback for docs not indexed on pkg.go.dev.
assert vs require
Both offer identical assertions. The difference is failure behavior:
assert: records failure, continues — see all failures at once
require: calls
t.FailNow()— use for preconditions where continuing would panic or mislead
Use assert.New(t) / require.New(t) for readability. Name them is and must:
func TestParseConfig(t *testing.T) {
is := assert.New(t)
must := require.New(t)
cfg, err := ParseConfig("testdata/valid.yaml")
must.NoError(err) // stop if parsing fails — cfg would be nil
must.NotNil(cfg)
is.Equal("production", cfg.Environment)
is.Equal(8080, cfg.Port)
is.True(cfg.TLS.Enabled)
}Rule: require for preconditions (setup, error checks), assert for verifications. Never mix randomly.
Core Assertions
is := assert.New(t)
// Equality
is.Equal(expected, actual) // DeepEqual + exact type
is.NotEqual(unexpected, actual)
is.EqualValues(expected, actual) // converts to common type first
is.EqualExportedValues(expected, actual)
// Nil / Bool / Emptiness
is.Nil(obj) is.NotNil(obj)
is.True(cond) is.False(cond)
is.Empty(collection) is.NotEmpty(collection)
is.Len(collection, n)
// Contains (strings, slices, map keys)
is.Contains("hello world", "world")
is.Contains([]int{1, 2, 3}, 2)
is.Contains(map[string]int{"a": 1}, "a")
// Comparison
is.Greater(actual, threshold) is.Less(actual, ceiling)
is.Positive(val) is.Negative(val)
is.Zero(val)
// Errors
is.Error(err) is.NoError(err)
is.ErrorIs(err, ErrNotFound) // walks error chain
is.ErrorAs(err, &target)
is.ErrorContains(err, "not found")
// Type
is.IsType(&User{}, obj)
is.Implements((*io.Reader)(nil), obj)Argument order: always (expected, actual) — swapping produces confusing diff output.
Advanced Assertions
is.ElementsMatch([]string{"b", "a", "c"}, result) // unordered comparison
is.InDelta(3.14, computedPi, 0.01) // float tolerance
is.JSONEq(`{"name":"alice"}`, `{"name": "alice"}`) // ignores whitespace/key order
is.WithinDuration(expected, actual, 5*time.Second)
is.Regexp(`^user-[a-f0-9]+$`, userID)
// Async polling
is.Eventually(func() bool {
status, _ := client.GetJobStatus(jobID)
return status == "completed"
}, 5*time.Second, 100*time.Millisecond)
// Async polling with rich assertions
is.EventuallyWithT(func(c *assert.CollectT) {
resp, err := client.GetOrder(orderID)
assert.NoError(c, err)
assert.Equal(c, "shipped", resp.Status)
}, 10*time.Second, 500*time.Millisecond)testify/mock
Mock interfaces to isolate the unit under test. Embed mock.Mock, implement methods with m.Called(), always verify with AssertExpectations(t).
Key matchers: mock.Anything, mock.AnythingOfType("T"), mock.MatchedBy(func). Call modifiers: .Once(), .Times(n), .Maybe(), .Run(func).
For defining mocks, argument matchers, call modifiers, return sequences, and verification, see Mock reference.
testify/suite
Suites group related tests with shared setup/teardown.
Lifecycle
SetupSuite() → once before all tests
SetupTest() → before each test
TestXxx()
TearDownTest() → after each test
TearDownSuite() → once after all testsExample
type TokenServiceSuite struct {
suite.Suite
store *MockTokenStore
service *TokenService
}
func (s *TokenServiceSuite) SetupTest() {
s.store = new(MockTokenStore)
s.service = NewTokenService(s.store)
}
func (s *TokenServiceSuite) TestGenerate_ReturnsValidToken() {
s.store.On("Save", mock.Anything, mock.Anything).Return(nil)
token, err := s.service.Generate("user-42")
s.NoError(err)
s.NotEmpty(token)
s.store.AssertExpectations(s.T())
}
// Required launcher
func TestTokenServiceSuite(t *testing.T) {
suite.Run(t, new(TokenServiceSuite))
}Suite methods like s.Equal() behave like assert. For require: s.Require().NotNil(obj).
Common Mistakes
Forgetting
AssertExpectations(t)— mock expectations silently pass without verificationis.Equal(ErrNotFound, err)— fails on wrapped errors. Useis.ErrorIsto walk the chainSwapped argument order — testify assumes
(expected, actual). Swapping produces backwards diffsassertfor guards — test continues after failure and panics on nil dereference. UserequireMissing
suite.Run()— without the launcher function, zero tests execute silentlyComparing pointers —
is.Equal(ptr1, ptr2)compares addresses. Dereference or useEqualExportedValues
Linters
Use testifylint to catch wrong argument order, assert/require misuse, and more. See samber/cc-skills-golang@golang-lint skill.
Cross-References
→ See
samber/cc-skills-golang@golang-testingskill for general test patterns, table-driven tests, and CI→ See
samber/cc-skills-golang@golang-lintskill for testifylint configuration
Files
- ATTRIBUTION.mdAdded by AutoGineer926 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md7.2 KiB
- evals/evals.json11.9 KiB
- references/mock.md3.1 KiB
evals/evals.json
[
{
"id": 1,
"name": "assert-vs-require-precondition",
"description": "Tests whether the model uses require for preconditions and assert for verifications, not mixing them randomly",
"prompt": "Write a Go test using testify that parses a JSON config file, checks it has no error, verifies the config is not nil, then checks that config.Port equals 8080, config.Host equals 'localhost', and config.Debug is false.",
"trap": "Model may use assert for the error check and nil check (preconditions), which would cause a nil pointer panic on subsequent assertions if parsing fails",
"assertions": [
{"id": "1.1", "text": "Uses require (not assert) for the NoError check on parsing"},
{"id": "1.2", "text": "Uses require (not assert) for the NotNil check on config"},
{"id": "1.3", "text": "Uses assert for the subsequent value checks (Port, Host, Debug)"},
{"id": "1.4", "text": "Does NOT use require for all assertions indiscriminately"},
{"id": "1.5", "text": "Argument order is (expected, actual) not (actual, expected) for Equal calls"}
]
},
{
"id": 2,
"name": "assert-new-naming-convention",
"description": "Tests the skill's specific naming convention: 'is' for assert.New(t) and 'must' for require.New(t)",
"prompt": "I'm writing Go tests with testify and I find the repeated 't' parameter verbose. How can I make my assertions more readable? Show me an example with both assert and require.",
"trap": "Model may use generic variable names like 'a' and 'r', or 'assertions' and 'requirements' instead of the skill's recommended 'is' and 'must' convention",
"assertions": [
{"id": "2.1", "text": "Uses assert.New(t) to create a reusable assertion object"},
{"id": "2.2", "text": "Uses require.New(t) to create a reusable require object"},
{"id": "2.3", "text": "Names the assert.New(t) variable 'is'"},
{"id": "2.4", "text": "Names the require.New(t) variable 'must'"},
{"id": "2.5", "text": "Shows the 'is' and 'must' variables being used for different purposes (preconditions vs verifications)"}
]
},
{
"id": 3,
"name": "error-chain-assertion",
"description": "Tests knowledge that is.Equal(ErrNotFound, err) fails on wrapped errors and ErrorIs should be used instead",
"prompt": "I have a Go function that returns wrapped errors using fmt.Errorf with %w. Write a test that checks whether the returned error is ErrNotFound. The function signature is: func FindUser(id string) (*User, error)",
"trap": "Model may use assert.Equal(ErrNotFound, err) which fails on wrapped errors instead of assert.ErrorIs",
"assertions": [
{"id": "3.1", "text": "Uses ErrorIs (not Equal) to check the error against ErrNotFound"},
{"id": "3.2", "text": "Does NOT use assert.Equal or is.Equal to compare errors directly"},
{"id": "3.3", "text": "Uses require for the initial error existence check if subsequent assertions depend on it"},
{"id": "3.4", "text": "Argument order for ErrorIs is (err, target) not (target, err)"}
]
},
{
"id": 4,
"name": "mock-assert-expectations",
"description": "Tests whether AssertExpectations is called — without it, mock expectations silently pass",
"prompt": "Create a Go test using testify/mock for a NotificationService that calls a Sender.Send method. The test should verify that Send is called exactly once with the right email address.",
"trap": "Model may set up On().Return() expectations but forget to call AssertExpectations(t), making the test pass even if Send is never called",
"assertions": [
{"id": "4.1", "text": "Mock embeds mock.Mock"},
{"id": "4.2", "text": "Mock method uses m.Called() to forward arguments"},
{"id": "4.3", "text": "Test calls m.AssertExpectations(t) to verify all expectations were met"},
{"id": "4.4", "text": "Uses .Once() or equivalent call modifier to enforce exactly one call"},
{"id": "4.5", "text": "Uses mock.Anything for arguments that don't need specific matching (e.g., context)"}
]
},
{
"id": 5,
"name": "mock-matched-by-predicate",
"description": "Tests knowledge of mock.MatchedBy for custom argument matching beyond exact equality",
"prompt": "I have a mock for a Logger interface with method Log(ctx context.Context, entry LogEntry). I need to verify that the LogEntry has Level='error' and Message contains 'timeout', but I don't care about the exact timestamp. How do I write the mock expectation?",
"trap": "Model may try to match the entire LogEntry struct exactly (which fails due to timestamp) instead of using mock.MatchedBy with a predicate function",
"assertions": [
{"id": "5.1", "text": "Uses mock.MatchedBy with a predicate function for the LogEntry argument"},
{"id": "5.2", "text": "The predicate checks Level == 'error'"},
{"id": "5.3", "text": "The predicate checks that Message contains 'timeout' (using strings.Contains or similar)"},
{"id": "5.4", "text": "Uses mock.Anything for the context argument"},
{"id": "5.5", "text": "Calls AssertExpectations at the end"}
]
},
{
"id": 6,
"name": "mock-retry-different-returns",
"description": "Tests knowledge of chaining .Once() calls to return different values per call for retry testing",
"prompt": "I need to test that my Go HTTP client retries on failure. The client calls Fetcher.Fetch(url string) ([]byte, error). First call should return a timeout error, second call should succeed with some data. How do I set up this mock?",
"trap": "Model may not know how to return different values per call and instead use a single Return() that applies to all calls",
"assertions": [
{"id": "6.1", "text": "Sets up first On().Return() with an error and .Once()"},
{"id": "6.2", "text": "Sets up second On().Return() with success data and .Once()"},
{"id": "6.3", "text": "The two expectations are on the same method with the same arguments"},
{"id": "6.4", "text": "Calls AssertExpectations to verify both calls happened"}
]
},
{
"id": 7,
"name": "suite-lifecycle-and-launcher",
"description": "Tests that suite requires a launcher function (TestXxxSuite) and understands the lifecycle order",
"prompt": "Convert these flat Go tests into a testify suite. The tests share a database connection setup and cleanup. There are 3 test functions that all need a fresh mock store before each test.\n\n```go\nfunc TestCreateUser(t *testing.T) { ... }\nfunc TestDeleteUser(t *testing.T) { ... }\nfunc TestListUsers(t *testing.T) { ... }\n```",
"trap": "Model may create the suite struct and test methods but forget the launcher function (func TestXxxSuite(t *testing.T) { suite.Run(t, new(Suite)) }), causing zero tests to run",
"assertions": [
{"id": "7.1", "text": "Creates a suite struct embedding suite.Suite"},
{"id": "7.2", "text": "Uses SetupTest (not SetupSuite) for per-test mock store initialization"},
{"id": "7.3", "text": "Includes a launcher function: func TestXxxSuite(t *testing.T) with suite.Run()"},
{"id": "7.4", "text": "Test methods are named TestXxx (starting with Test) on the suite receiver"},
{"id": "7.5", "text": "Uses SetupSuite or TearDownSuite for the shared database connection (one-time setup)"}
]
},
{
"id": 8,
"name": "suite-require-syntax",
"description": "Tests that suite methods use s.Require().NotNil() syntax for require behavior, since s.NotNil() is assert-style",
"prompt": "In my testify suite test method, I need to check that a database connection is not nil before proceeding. If it's nil, the test should stop immediately. How do I do a require-style assertion inside a suite?",
"trap": "Model may use s.NotNil() thinking it acts like require, but suite methods default to assert behavior. Must use s.Require().NotNil() for fail-fast",
"assertions": [
{"id": "8.1", "text": "Uses s.Require().NotNil() (not just s.NotNil()) for fail-fast behavior"},
{"id": "8.2", "text": "Explains that s.NotNil() and similar suite methods behave like assert (continue on failure)"},
{"id": "8.3", "text": "Shows that s.Require() returns a require-style assertion object"}
]
},
{
"id": 9,
"name": "pointer-comparison-trap",
"description": "Tests awareness that is.Equal(ptr1, ptr2) compares addresses, not values",
"prompt": "I have two *User pointers pointing to different structs with the same field values. My test `assert.Equal(t, user1, user2)` is failing. Both users have Name='Alice' and Age=30. What's wrong?",
"trap": "Model may suggest various debugging approaches without identifying the core issue: Equal on pointers compares addresses",
"assertions": [
{"id": "9.1", "text": "Identifies that assert.Equal on pointers compares memory addresses, not struct values"},
{"id": "9.2", "text": "Recommends dereferencing the pointers (e.g., assert.Equal(t, *user1, *user2)) or using EqualExportedValues"},
{"id": "9.3", "text": "Mentions EqualExportedValues as an alternative for comparing only exported fields"}
]
},
{
"id": 10,
"name": "eventually-with-rich-assertions",
"description": "Tests knowledge of EventuallyWithT for async polling with multiple rich assertions (not just bool)",
"prompt": "I need to test an async job processor. After submitting a job, I need to poll until the job status is 'completed' AND the result count is greater than 0. The polling should timeout after 10 seconds. How do I write this test with testify?",
"trap": "Model may use Eventually with a simple bool function, which only checks one condition and loses assertion error messages. EventuallyWithT allows multiple rich assertions.",
"assertions": [
{"id": "10.1", "text": "Uses EventuallyWithT (not just Eventually) for rich assertions"},
{"id": "10.2", "text": "The callback receives *assert.CollectT (or similar collect parameter)"},
{"id": "10.3", "text": "Multiple assertions are made inside the callback (status check AND result count check)"},
{"id": "10.4", "text": "Uses assert.NoError/assert.Equal with the CollectT parameter inside the callback, not with t"},
{"id": "10.5", "text": "Specifies timeout (10s) and polling interval as separate parameters"}
]
},
{
"id": 11,
"name": "testifylint-recommendation",
"description": "testifylint catches testify-specific mistakes that generic linters miss; model should recommend it over manual code review for testify patterns",
"prompt": "Our team's test code review keeps catching the same testify mistakes:\n\n1. `assert.Equal(t, err, ErrNotFound)` instead of `assert.ErrorIs(t, err, ErrNotFound)`\n2. `assert.Equal(t, got, want)` where expected/actual are swapped\n3. Using `assert.NoError(t, err)` instead of `require.NoError(t, err)` before dereferencing a pointer\n4. `assert.Equal(t, true, someCondition)` instead of `assert.True(t, someCondition)`\n\nOur lead says: 'These are all discipline issues — we should just review more carefully and add examples to our style guide.' Is there a better automated solution?",
"trap": "The lead's position (careful review + style guide) sounds reasonable for a small team. The model should recognize that testifylint catches all four of these patterns automatically, making manual review for them unnecessary. Without the skill, the model may agree with the lead or only suggest generic linters like staticcheck.",
"assertions": [
{"id": "11.1", "text": "Recommends testifylint specifically — explains that it is designed to catch exactly the patterns described (not just generic Go linters like staticcheck or golangci-lint defaults)"},
{"id": "11.2", "text": "Pushes back on the lead's 'review more carefully' approach — automated linting is more reliable than manual discipline for mechanical patterns"},
{"id": "11.3", "text": "Confirms testifylint catches at least two of the four described patterns: wrong argument order (expected/actual swap) and assert/require misuse (using assert before pointer dereference)"}
]
}
]
Requirements and compatibility
Written for: Claude Code, OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Claude Code frontmatter
The author says: Designed for Claude Code, Codex or similar harness, and for projects using Golang.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- samber
- Publisher
- Samuel Berthe (Community)
- Distributed by
- Samuel Berthe
- Source
- samber/cc-skills-golang · skills/golang-stretchr-testify · commit bb1d43d
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by samber or Samuel Berthe.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download golang-stretchr-testify.zip