RepoPilotOpen in app →

stretchr/testify

A toolkit with common assertions and mocks that plays nicely with the standard library

Healthy

Healthy across the board

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 2d ago
  • 16 active contributors
  • Distributed ownership (top contributor 34% of recent commits)
Show 3 more →
  • MIT licensed
  • CI configured
  • Tests present

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/stretchr/testify)](https://repopilot.app/r/stretchr/testify)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/stretchr/testify on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: stretchr/testify

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/stretchr/testify shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 2d ago
  • 16 active contributors
  • Distributed ownership (top contributor 34% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live stretchr/testify repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/stretchr/testify.

What it runs against: a local clone of stretchr/testify — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in stretchr/testify | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 32 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>stretchr/testify</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of stretchr/testify. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/stretchr/testify.git
#   cd testify
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of stretchr/testify and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "stretchr/testify(\\.git)?\\b" \\
  && ok "origin remote is stretchr/testify" \\
  || miss "origin remote is not stretchr/testify (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "assert/assertions.go" \\
  && ok "assert/assertions.go" \\
  || miss "missing critical file: assert/assertions.go"
test -f "mock/mock.go" \\
  && ok "mock/mock.go" \\
  || miss "missing critical file: mock/mock.go"
test -f "suite/suite.go" \\
  && ok "suite/suite.go" \\
  || miss "missing critical file: suite/suite.go"
test -f "require/require.go" \\
  && ok "require/require.go" \\
  || miss "missing critical file: require/require.go"
test -f "assert/assertion_compare.go" \\
  && ok "assert/assertion_compare.go" \\
  || miss "missing critical file: assert/assertion_compare.go"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 32 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~2d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/stretchr/testify"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Testify is a Go testing toolkit that provides assertion helpers, mock objects, and test suite utilities to make writing idiomatic Go tests more ergonomic and readable. It wraps the standard testing.T interface with fluent assertion methods like assert.Equal(), assert.NotNil(), and assert.Contains(), plus HTTP testing utilities and a mock package for behavior verification. Flat multi-package structure: assert/ contains 15+ assertion function files (assertions.go, assertion_compare.go, assertion_format.go) generated via _codegen/main.go, http/ provides TestResponseWriter for HTTP testing, mock/ (not in top 60 but implied in README) handles mocking, and suite/ offers test suite patterns. Code generation templates (.tmpl files) drive much of the repetitive assertion boilerplate.

👥Who it's for

Go developers writing unit and integration tests who want readable, maintainable test code without verbose error checking. Library maintainers and application engineers use testify to reduce boilerplate in *_test.go files across thousands of Go projects.

🌱Maturity & risk

Testify is mature and stable: it has 16k+ GitHub stars, is actively maintained at v1 with a no-breaking-changes commitment (as stated in the README), has comprehensive CI via GitHub Actions (.github/workflows/main.yml and release.yml), and a full test suite (assert/assertions_test.go, assert/http_assertions_test.go). Production-ready and battle-tested across the Go ecosystem.

Risk is minimal: the project explicitly freezes at v1 to prevent breaking changes (mentioned in README and MAINTAINERS.md), has a small but dedicated maintainer group, and no external dependencies (go.mod only lists test dependencies). The main risk is that v1-only policy means new features require consensus, which can slow adoption of modern Go testing patterns.

Active areas of work

The project is in stable maintenance mode: no major features are being added (v1 freeze), but bug fixes and test coverage improvements continue. The presence of .ci.gofmt.sh, .ci.govet.sh, and .ci.readme.fmt.sh indicates active linting and formatting governance. Dependabot is configured (.github/dependabot.yml) to keep dependencies fresh.

🚀Get running

git clone https://github.com/stretchr/testify.git
cd testify
go test ./...
go run ./_codegen/main.go  # regenerate assertion code if modifying templates

Daily commands: This is a library, not a runnable application. Run tests with go test ./... or go test ./assert, go test ./http, etc. Run linters with the shell scripts: bash .ci.govet.sh, bash .ci.gofmt.sh.

🗺️Map of the codebase

  • assert/assertions.go — Core assertion functions that form the public API; this is the most-used package for most developers.
  • mock/mock.go — Mock implementation and Call tracking; essential for understanding how testify handles mocking behavior.
  • suite/suite.go — Suite struct and test lifecycle management; required reading for anyone using testify's suite pattern.
  • require/require.go — Fatal assertion variant; generated from templates but defines the strict assertion API contract.
  • assert/assertion_compare.go — Comparison assertion logic for equality, ordering, and type checks; high-complexity core logic.
  • _codegen/main.go — Code generation for require and forward assertion packages; must understand before modifying assertion APIs.
  • doc.go — Package-level documentation and architecture overview; entry point for understanding the whole toolkit.

🧩Components & responsibilities

  • assert package (Go testing.T, internal/spew, internal/difflib) — Provides non-fatal assertion functions; logs failures via t.Error but does not stop test execution
    • Failure mode: Assertion logs error message and test continues; overall test fails if any assertion fails
  • require package (Go testing.T, internal/spew, code-generated from templates) — Provides fatal assertion functions; stops test execution immediately on failure via t.FailNow()
    • Failure mode: First failed assertion stops the test; useful for preventing cascading failures in dependent assertions
  • mock package (Go reflect, sync.Mutex for thread-safety) — Provides Mock type for recording and verifying method call expectations; does not auto-generate mock implementations
    • Failure mode: AssertExpectations fails if actual calls do not match On() expectations; mock panics on unexpected calls if Run() is set
  • suite package (Go testing.T, reflect for method discovery) — Provides test organization with lifecycle hooks; runs all Test* methods and reports statistics
    • Failure mode: If SetupSuite fails, no tests run; if BeforeTest fails, that test is skipped
  • http package — Provides TestResponseWriter and TestRoundTri

🛠️How to make changes

Add a New Assertion Function

  1. Add the assertion logic to assert/assertions.go with signature func AssertXxx(t TestingT, ...) bool (assert/assertions.go)
  2. Implement the fatal variant by checking if codegen generates it from require_forward.go.tmpl (require/require.go.tmpl)
  3. Add forward/chaining variant in assertion_forward.go.tmpl if supporting fluent API (assert/assertion_forward.go.tmpl)
  4. Run _codegen/main.go to regenerate require.go and forward_assertions.go (_codegen/main.go)
  5. Write tests in assert/assertions_test.go following existing test patterns (assert/assertions_test.go)

Create a New Test Suite

  1. Define a type that embeds suite.Suite to inherit test hooks (suite/suite.go)
  2. Implement SetupSuite() and TeardownSuite() for one-time setup (optional) (suite/suite.go)
  3. Implement BeforeTest(suiteName, testName string) and AfterTest() for per-test hooks (optional) (suite/suite.go)
  4. Add test methods with signature func (s YourSuite) Test(). Use assert or require within them. (assert/assertions.go)
  5. Call suite.Run(t, &YourSuite{}) in your *_test.go file to execute the suite (suite/suite.go)

Add Mock Expectations to a Test

  1. Create a *mock.Mock instance or embed it in a mock type (mock/mock.go)
  2. Use mock.On("MethodName", args...).Return(values...) to set expectations (mock/mock.go)
  3. Pass the mock to your code under test and execute it (mock/mock.go)
  4. Call mock.AssertExpectations(t) to verify all mocked methods were called as expected (mock/mock.go)
  5. Use mock.AssertNotCalled(t, "UnwantedMethod") for negative assertions if needed (mock/mock.go)

🔧Why these technologies

  • Go testing package integration — testify wraps *testing.T to provide a consistent assertion API that fits Go's standard testing patterns
  • Code generation (via _codegen) — DRY principle—generate require.go and forward_assertions.go from templates to avoid duplicating assertion logic across variants
  • Vendored spew and difflib — Provides readable diff output and value inspection without adding external dependencies to users
  • Mock with Call tracking — Enables behavior-driven testing patterns where code can verify not just return values but method call sequences and arguments

⚖️Trade-offs already made

  • Maintain v1 compatibility with no breaking changes

    • Why: Testify is established in production codebases; stability is more important than new features
    • Consequence: New assertion functions cannot change existing signatures; code generation ensures consistency
  • Separate assert and require packages

    • Why: Allows developers to choose failure semantics: continue-on-fail (assert) vs. fail-fast (require)
    • Consequence: API surface is doubled; code generation mitigates maintenance burden but adds build complexity
  • Embed Mock type rather than interface-based mocking

    • Why: Simpler API that doesn't require reflection or code generation for mocks; more discoverable
    • Consequence: Users must manually define mock methods rather than auto-generating them from interfaces

🚫Non-goals (don't propose these)

  • Does not provide property-based testing (no QuickCheck equivalent)
  • Does not handle benchmark assertion or performance profiling
  • Does not provide test data factories or fixtures beyond basic mock utilities
  • Does not generate test code from interfaces or structs automatically

🪤Traps & gotchas

Code generation required: modifying assertion templates in assert/*.tmpl requires running go run ./_codegen/main.go to regenerate the assertion functions; forgetting this breaks consistency. Module structure: the _codegen directory is a separate Go module (go.mod at _codegen/go.mod), not the main module, so it's run as a tool. v1 freeze ideology: feature requests may be rejected per the v2 discussion link (github.com/stretchr/testify/discussions/1560); understand this before proposing new assertion types.

🏗️Architecture

💡Concepts to learn

  • Type assertion and reflection-based comparison — Testify's assertion_compare.go uses reflect.DeepEqual and type-safe comparisons to handle assertions across arbitrary Go types; understanding this is key to modifying or extending comparison logic
  • Code generation via Go templates — The _codegen tool and *.tmpl files in assert/ generate repetitive assertion variants (Equal, Equalf, etc.) to avoid manual duplication; this pattern is critical to understanding how testify scales assertion APIs
  • Interface-based testing (testing.T and Asserter interface) — Testify's assertions accept any type implementing a minimal interface, not just testing.T, allowing reuse in custom test runners and assertion chains via forward.go
  • Mock assertion (behavior verification via Call tracking) — The mock package tracks method calls and arguments to verify object interactions; understanding call stacks and assertion replay is essential for using testify's mocking capabilities
  • HTTP testing via ResponseWriter mocking — http/test_response_writer.go implements net/http.ResponseWriter to capture and assert HTTP responses without a live server; this pattern is crucial for handler unit testing
  • Fluent assertion chaining and return values — Every assertion returns bool to enable conditional chaining (e.g., if assert.NotNil(t, obj) { assert.Equal(...) }); this API design allows test code to be more readable and avoid nil panics
  • Test suite pattern (Suite interface) — The suite package provides SetupTest, TeardownTest hooks and test method discovery via reflection; this is testify's answer to structured test organization beyond flat function lists
  • golang/go — The Go standard library that testify wraps and extends; understanding testing.T and the testing package is prerequisite knowledge
  • google/gotest.tools — Alternative Go testing helpers (part of gotest.tools/assert) with different philosophy; comparison point for assertion API design
  • antonboom/testifylint — Golangci-lint plugin that enforces testify best practices (recommended in README); detects common misuses of testify functions
  • stretchr/objx — Companion library by same author for fluent object access in tests; often used alongside testify for complex assertion scenarios
  • smartystreets/goconvey — Alternative BDD-style testing framework for Go; different assertion syntax and test organization vs testify's function-based approach

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive tests for assert/yaml directory

The assert/yaml directory contains yaml_custom.go, yaml_default.go, and yaml_fail.go but there are no corresponding test files (_test.go). This is a critical gap for a testing toolkit. Adding tests would ensure YAML assertion functionality works correctly and prevent regressions.

  • [ ] Create assert/yaml/yaml_custom_test.go with tests for custom YAML unmarshaling behavior
  • [ ] Create assert/yaml/yaml_default_test.go with tests for default YAML unmarshaling
  • [ ] Create assert/yaml/yaml_fail_test.go with tests for expected failure cases
  • [ ] Ensure tests cover edge cases like malformed YAML, type mismatches, and nested structures
  • [ ] Run tests locally and verify coverage meets project standards

Extend http package with response assertion helpers

The http/test_response_writer.go and http/test_round_tripper.go provide HTTP testing utilities, but there are no corresponding assertion helpers (like assert/http_assertions.go pattern exists). Adding HTTP-specific assertion functions would make HTTP testing easier for users.

  • [ ] Create http/http_assertions.go with assertion helpers (e.g., AssertStatusCode, AssertHeaderPresent, AssertBodyContains)
  • [ ] Create http/http_assertions_test.go with comprehensive tests for each assertion function
  • [ ] Consider mirroring the forward assertions pattern used in assert/forward_assertions.go for fluent API support
  • [ ] Add examples and documentation strings for new assertion functions
  • [ ] Update assert/doc.go or create http/doc.go to document the new HTTP assertions package

Add code generation templates for additional assertion types

The _codegen directory contains infrastructure for generating assertion code (main.go, imports.go, and templates like assertion_format.go.tmpl and assertion_forward.go.tmpl). However, there are likely assertion types not yet covered by generation. Adding new templates would reduce code duplication and ensure consistency.

  • [ ] Analyze assert/assertions.go to identify assertion functions not matching template patterns
  • [ ] Create _codegen/templates/ directory if it doesn't exist, moving existing .tmpl files there
  • [ ] Add new template files for missing assertion patterns (e.g., assertion_json.go.tmpl, assertion_file.go.tmpl)
  • [ ] Update _codegen/main.go to process new templates and generate corresponding assertion files
  • [ ] Run .ci.gogenerate.sh and verify generated files match expected output
  • [ ] Update CONTRIBUTING.md to document the code generation process for future maintainers

🌿Good first issues

  • Add YAML assertion helpers to assert/yaml/ directory: currently only yaml_default.go, yaml_custom.go, yaml_fail.go exist with minimal coverage; implement AssertYAMLEqual and related functions with corresponding tests
  • Expand http_assertions_test.go coverage: the http assertions package has assertions.go but tests are sparse; add table-driven test cases for edge cases like nil responses, malformed headers, and status code ranges
  • Document the code generation workflow: the _codegen/ tool is powerful but undocumented; add comments to _codegen/main.go and a CONTRIBUTING.md section explaining how to add new assertion families via templates

Top contributors

Click to expand

📝Recent commits

Click to expand
  • c920f80 — Merge pull request #1883 from SuperQ/superq/pin_actions (dolmen)
  • 21beeae — Pin GitHub Actions hashes (SuperQ)
  • d75b707 — Merge pull request #1823 from ccoveille-forks/bump-objx (dolmen)
  • 5f80e4a — Merge pull request #1856 from alexandear-org/chore-add-go-1-24 (ccoVeille)
  • b13d7e3 — CI: test also with Go 1.24 (alexandear)
  • f58776d — deps: bump objx to v0.5.3 and remove dependency cycle issue (ccoVeille)
  • c7007d3 — Merge pull request #1812 from stretchr/EMERITUS-tylerb (dolmen)
  • 30c1157 — Merge branch 'master' into EMERITUS-tylerb (dolmen)
  • cddda8c — Merge pull request #1842 from a2not/fix-EventuallyWithTf-doc-callback-signature (dolmen)
  • 967a7b7 — fix: update EventuallyWithTf comment examples and codegen (a2not)

🔒Security observations

The testify codebase demonstrates strong security practices overall. It is a testing/assertion library with no apparent injection vulnerabilities, hardcoded secrets, or exposed credentials. The main concerns are around the two vendored internal packages (spew and difflib) that require manual security maintenance. The project follows standard Go best practices with proper CI/CD pipelines, dependency management via go.mod/go.sum, and organized code structure. No critical or high-severity vulnerabilities were identified in the static analysis.

  • Low · Vendored internal/spew package — internal/spew/. The internal/spew package is a vendored dependency (indicated by the LICENSE and README files in internal/spew). While this approach ensures stability, it means security updates to the original spew library must be manually merged. The package is used for dumping and formatting data structures, which could theoretically process untrusted input if misused. Fix: Regularly monitor the upstream spew repository for security updates. Consider implementing a process to check and merge critical security patches. Document the vendoring decision and maintenance plan.
  • Low · Vendored internal/difflib package — internal/difflib/. The internal/difflib package is vendored with its own LICENSE file, indicating it's a fork or copy of an external library. Like spew, security updates must be manually tracked and applied. Fix: Establish a maintenance process for monitoring the upstream difflib library for security issues. Document which upstream project is being vendored and maintain version tracking.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · stretchr/testify — RepoPilot