📖 9 min read (~ 1900 words).

Changes from v1

Summary

Key Changes:

  • Dependencies: Zero external (internalized 2, optional 1 via enable pattern)
  • New functions: 51 total (38 generic + 13 reflection-based)
  • Performance: ~10x for generic variants (from 1.2x to 81x, your mileage may vary)
  • Architecture: 100% code generation from single source
  • Breaking changes: Requires go1.24, removed suites, mocks, http tooling, and deprecated functions. YAMLEq becomes optional (panics by default).

Testify v2 represents a comprehensive modernization

  • Zero Dependencies: Completely self-contained
  • Type Safety: 38 generic assertions catch errors at compile time
  • Performance: Up to 81x faster with generics
  • Documentation: compelling Hugo site to document the API by use-case domain
  • Quality: 96% test coverage, extensive fuzzing & benchmarking
  • Maintainability: 100% code generation from single source

This fork maintains compatibility where possible while making bold improvements in architecture, safety, and performance.

Fork Information:

See also a quick migration guide.

Cross-Domain Changes

Major Additions

Usage

ChangeOriginDescription
Generic assertionsMultiple upstream proposalsAdded 38 type-safe assertion functions with T suffix across 10 domains
Zero dependenciesDesign goalInternalized go-spew and difflib; removed all external dependencies
Optional YAML supportDesign goalYAML assertions are now enabled via opt-in enable/yaml module
Colorized output#1467, #1480, #1232, #994Optional colorization via enable/color module with themes
Enhanced diff output#1829Improved time.Time rendering, deterministic map ordering

Maintenability

ChangeOriginDescription
Code generationDesign goal100% generated assert/require packages (608+ functions from 76 assertions)
Code modernizationDesign goalRelinted, refactored and modernized the code base, including internalized difflib and go-spew
Refactored testsDesign goalFull refactoring of tests on assertion functions, with unified test scenarios for reflection-based/generic assertions

Major Removals (Breaking Changes)

RemovedReason
Suite packageComplex interactions with dependencies; might re-introduce this feature later
Mock packageUse specialized mockery tool instead
HTTP packageSimplified focus; may be reintroduced later
Deprecated functionsClean slate for v2
RenamingNoDirExists renamed into DirNotExists. NoFileExists renamed into FileNotExists

Infrastructure Improvements

ChangeDescription
Internalized dependenciesgo-spew and difflib internalized with modernized code
Module structureClean separation: core (zero deps), enable modules (optional)
Documentation siteHugo-based site with domain-organized API reference
Fuzz testingFuzz test on spew.Sdump based on random data structures generation
Comprehensive benchmarks37 benchmarks comparing generic vs reflection performance
Advanced CIReuse go-openapi workflows with tests and coverage reporting, fuzz testing, release automation

Bug Fixes and Safety Improvements

Critical Fixes reported upstream

Issue/PRDomainDescription
#1223DisplayDisplay uint values in decimal instead of hex
#1611ConditionFixed goroutine leak in Eventually/Never
#1813Internal (spew)Fixed panic with unexported fields (via #1828)
#1818StringFixed panic on invalid regex in Regexp/NotRegexp
#1822Internal (spew)Deterministic map ordering in diffs
#1825EqualityFixed panic when using EqualValues with uncomparable types
#1828Internal (spew)Fixed panic with unexported fields in maps

Comprehensive Spew Testing

  • Added property-based fuzzing for go-spew with random type generator
  • Fixed circular reference edge cases (pointer wrapped in interface, circular map reference)
  • Supersedes upstream #1824

Reflection Safety

  • More defensive guards re-reflect panic risk in EqualExportedValues
  • Fixed 50 unchecked type assertions across test codebase
  • Zero linting issues with forcetypeassert linter

Changes by Domain

Boolean

Generics

New Generic Functions (2)

FunctionTypeOriginDescription
TrueT[B ~bool]GenericGenerics initiativeType-safe boolean true assertion
FalseT[B ~bool]GenericGenerics initiativeType-safe boolean false assertion

Behavior changes: None

Collection

Generics

New Generic Functions (12)

FunctionType ParametersDescription
StringContainsT[S Text]String or []byteType-safe string/bytes contains check
StringNotContainsT[S Text]String or []byteType-safe string/bytes not-contains check
SliceContainsT[E comparable]Comparable elementType-safe slice membership check
SliceNotContainsT[E comparable]Comparable elementType-safe slice non-membership check
MapContainsT[K comparable, V any]Key typeType-safe map key check
MapNotContainsT[K comparable, V any]Key typeType-safe map key absence check
SeqContainsT[E comparable]Iterator elementType-safe iterator membership check (Go 1.23+)
SeqNotContainsT[E comparable]Iterator elementType-safe iterator non-membership check (Go 1.23+)
ElementsMatchT[E comparable]Slice elementType-safe slice equality (any order)
NotElementsMatchT[E comparable]Slice elementType-safe slice inequality
SliceSubsetT[E comparable]Slice elementType-safe subset relationship check
SliceNotSubsetT[E comparable]Slice elementType-safe non-subset check

Origin: Generic initiative + #1685 (partial - SeqContains variants only)

Performance: 16-81x faster than reflection-based variants (see benchmarks)

Behavior changes: None

Comparison

Generics

New Generic Functions (6)

FunctionType ParametersDescription
GreaterT[V Ordered]extended Ordered type1Type-safe greater-than comparison
GreaterOrEqualT[V Ordered]Ordered typeType-safe >= comparison
LessT[V Ordered]Ordered typeType-safe less-than comparison
LessOrEqualT[V Ordered]Ordered typeType-safe <= comparison
PositiveT[V Ordered]Ordered typeType-safe positive value check (> 0)
NegativeT[V Ordered]Ordered typeType-safe negative value check (< 0)

Origin: Generics initiative

Performance: 10-22x faster than reflection-based variants


  1. Ordered is defined as the union of standard go ordered types, plus []byte and time.Time↩︎

Behavior changes: None

Condition

New functions: None

⚠️ Behavior Changes

ChangeOriginDescription
Fixed goroutine leak#1611Consolidated Eventually, Never, and EventuallyWithT into single pollCondition function
Context-based pollingInternal refactoringReimplemented with context-based approach for better resource management
Unified implementationInternal refactoringSingle implementation eliminates code duplication and prevents resource leaks

Impact: This fix eliminates goroutine leaks that could occur when using Eventually or Never assertions. The new implementation uses a context-based approach that properly manages resources and provides a cleaner shutdown mechanism. Callers should NOT assume that the call to Eventually or Never exits before the condition is evaluated. Callers should NOT assume that the call to Eventually or Never exits before the condition is evaluated.

Supersedes: This implementation also supersedes upstream proposals #1819 (handle unexpected exits) and #1830 (CollectT.Halt) with a more comprehensive solution.

Equality

Generics

New Generic Functions (4)

FunctionType ParametersDescription
EqualT[V comparable]Comparable typeType-safe equality check
NotEqualT[V comparable]Comparable typeType-safe inequality check
SameT[V comparable]Comparable typeType-safe pointer identity check
NotSameT[V comparable]Comparable typeType-safe pointer difference check

Origin: Generics initiative

Performance: 10-13x faster for Equal/NotEqual, 1.5-2x for Same/NotSame

⚠️ Behavior Changes

FunctionChangeReason
EqualValuesNow fails with function types (like Equal)#1825 - Consistency and safety
Same/NotSameTwo nil pointers of same type now correctly considered “same”Edge case fix

Error

New functions: None

Behavior changes: None

File

FunctionTypeOriginDescription
FileEmptyReflectionNew additionAssert file exists and is empty (0 bytes)
FileNotEmptyReflectionNew additionAssert file exists and is not empty

Note: DirExists was already present in upstream, NoDirExists renamed into DirNotExists. NoFileExists renamed into FileNotExists

Behavior changes: None

HTTP

New functions: None

Behavior changes: None

JSON

Generics

New Generic Function (1)

FunctionType ParametersDescription
JSONEqT[S Text]String or []byteType-safe JSON semantic equality

Performance: Comparable (JSON parsing dominates)

Reflection-based

New Reflection Function (1)

FunctionOriginDescription
JSONEqBytes#1513JSON equality for byte slices

Behavior changes: None

Number

Generics

New Generic Functions (2)

FunctionType ParametersDescription
InDeltaT[V Float|Integer]Numeric typeType-safe float comparison with absolute delta
InEpsilonT[V Float]Float typeType-safe float comparison with relative epsilon

Origin: Generics initiative

Performance: 1.2-1.5x faster

⚠️ Behavior Changes

  • Fixed IEEE 754 edge case handling (NaN, Inf)
  • Added support for zero expected value in InEpsilon (falls back to absolute error)
  • Fixed invalid type conversion for uintptr in reflect-based compare

Ordering

Generics

New Generic Functions (6)

FunctionType ParametersDescription
IsIncreasingT[E Ordered]Ordered[^1] slice elementType-safe strictly increasing check
IsDecreasingT[E Ordered]Ordered slice elementType-safe strictly decreasing check
IsNonIncreasingT[E Ordered]Ordered slice elementType-safe non-increasing check (allows equal)
IsNonDecreasingT[E Ordered]Ordered slice elementType-safe non-decreasing check (allows equal)
SortedT[E cmp.Ordered]Ordered slice elementType-safe sorted check (generic-only function)
NotSortedT[E cmp.Ordered]Ordered slice elementType-safe unsorted check (generic-only function)

Origin: Generics initiative

Performance: 6.5-9.5x faster

Note: SortedT and NotSortedT are generic-only (no reflection equivalents)

⚠️ Behavior Changes

FunctionChangeReason
IsNonDecreasingLogic corrected to match documentationInverted logic fixed
IsNonIncreasingLogic corrected to match documentationInverted logic fixed

Panic

New functions: None

Behavior changes: None

String

Generics

New Generic Functions (2)

FunctionType ParametersDescription
RegexpT[S Text]String or []byteType-safe regex match check
NotRegexpT[S Text]String or []byteType-safe regex non-match check

Origin: Generics initiative

Performance: 1.2x faster (regex compilation dominates)

⚠️ Behavior Changes

ChangeOriginDescription
Fix panic on invalid regex#1818Handle invalid regex patterns gracefully
Refactored regex handlingInternalFixed quirks with unexpected behavior on some input types

Testing

New functions: None

Behavior changes: None

Time

New functions: None

⚠️ Behavior Changes

ChangeOriginDescription
Fix time.Time rendering in diffs[#1829]Improved time display in failure messages

Type

Generics

New Generic Functions (2)

FunctionType ParametersDescription
IsOfTypeT[EType any]Expected typeType assertion without dummy value
IsNotOfTypeT[EType any]Expected typeNegative type assertion without dummy value

Origin: #1805 Performance: 9-11x faster

Reflection-based

New Reflection Functions (2)

FunctionOriginDescription
Kind#1803Assert value is of specific reflect.Kind
NotKind#1803Assert value is not of specific reflect.Kind

Behavior changes: None

YAML

Generics

New Generic Function (1)

FunctionType ParametersDescription
YAMLEqT[S Text]String or []byteType-safe YAML semantic equality

Performance: Comparable (YAML parsing dominates)

Reflection-based

New Reflection Function (1)

FunctionOriginDescription
YAMLEqBytesConsistencyYAML equality for byte slices (matches JSONEqBytes)

⚠️ Behavior Changes

Architecture change: YAML support is now opt-in via import _ "github.com/go-openapi/testify/v2/enable/yaml" Behavior changes: None

Other changes

Performance Improvements

See Performance Benchmarks for a detailed presentation.

Generic vs Reflection Performance

DomainFunctionSpeedupKey Benefit
CollectionElementsMatchT21-81xScales with collection size
EqualityEqualT10-13xZero allocations
ComparisonGreaterT/LessT10-22xZero allocations
CollectionSliceContainsT16xZero allocations
CollectionSeqContainsT25xIterator optimization
OrderingIsIncreasingT7-9xZero allocations
TypeIsOfTypeT9-11xNo reflection overhead

Memory savings: Up to 99% reduction in allocations for large collections

Architecture Changes

These affect the way the project is maintained, but not how it is used.

Code Generation

All assert and require packages are 100% generated from a single source:

  • Source: internal/assertions/ (~5,000 LOC)
  • Generated: ~600+ functions across assert/require packages
  • Variants: 8 variants per assertion (assert/require x standard/format/forward/forward+format), 4 variants for generic assertions (assert/require x standard/format)

NOTE: generic assertions obviously can’t be propagated as a “forward variant”, i.e as a method of the Assertion object.

Module Structure

The project adopts a mono-repo structure (with the appropriate changes made in CI).

This means that the github repo exposes several independant go modules.

github.com/go-openapi/testify/v2           # Core (zero deps) [go.mod]
├── assert/                                # Generated package
├── require/                               # Generated package
├── internal/                              # Internalized dependencies
│   ├── spew/                              # Internalized go-spew
│   ├── difflib/                           # Internalized go-difflib
│   └── assertions/                        # Single source of truth
├── enable/                                # Modules for optional features
│   ├── yaml/                              # Optional YAML support [go.mod]
│   └── color/                             # Optional colorization [go.mod]
│
└── codegen/                               # Code and documentation generator [go.mod]

Documentation

  • Hugo-based documentation site
  • Domain-organized API reference (18 domains)
  • Comprehensive examples and tutorials
  • Performance benchmarks

Project Metrics

MetricValue
New functions51 (38 generic + 13 reflection)
Total assertions76 base assertions
Generated functions~600 (76 × 8 variants - generics get 4 variants only)
Generic coverage10 domains
Performance improvement1.2x to 81x faster
Dependencies0 external (was 2 requiredl)
Test coverage96% overall, 100% on public APIs
Documentation domains18 logical categories