Testify v2

Info

This is the home of github.com/go-openapi/testify/v2, an active, opinionated fork of github.com/stretchr/testify.

Testify v2 - The v2 our tests wanted

A set of go packages that provide tools for testifying (verifying) that your code behaves as you intended.

This is the go-openapi fork of the great testify package.

Status

Fork me Design and exploration phase. Feedback, contributions and proposals are welcome.

See our ROADMAP.

Motivation

See why we wanted a v2.

Getting started

Import this library in your project like so.

go get github.com/go-openapi/testify/v2

… and start writing tests. Look at our examples.

Basic usage

testify simplifies your test assertions like so.

  • Standard library
    import (
            "testing"
        )
        ...
        
        const expected = "expected result"
    
    	  result := printImports(input)
    	  if result != expected {
    		  t.Errorf(
            "Expected: %s. Got: %s",
            expected, result, 
          )
    
          return
    	  }
  • testify
    import (
            "testing"
    
            "github.com/go-openapi/testify/v2/require"
        )
        ...
    
        const expected = "expected result"
    
    	  result := printImports(input)
    	  require.Equalf(t, expected, result,
            "Expected: %s. Got: %s", expected, result, 
        )

Usage with generics

Assertion functions that support go generic types are suffixed with T (for “Type safety”). A formatted variant suffixed with Tf is also exposed.

Obviously, the Assertion type cannot be extended with generic methods, as of go1.25.

  • EqualT
    import (
            "testing"
    
            "github.com/go-openapi/testify/v2/require"
        )
        ...
        
        const expected = "Hello World"
        var input := "World"
    
    	result := someRamblingTextGeneration(input)
        require.EqualT(t, expected, result)
  • InDeltaT
    import (
            "testing"
    
            "github.com/go-openapi/testify/v2/require"
        )
        ...
        
        const (
            expected = 1.00
            delta = 1E-6
        )
        var input = 1.01
    
    	result := someComplexComputation(input)
        require.InDeltaT(t, expected, input, delta)

Licensing

SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers

This library ships under the SPDX-License-Identifier: Apache-2.0.

See the license NOTICE, which recalls the licensing terms of all the pieces of software distributed with this fork, including internalized libraries.

Contributing

Feel free to submit issues, fork the repository and send pull requests!

Info

Code generation is used. Run go generate ./... to update generated files.

See also our CONTRIBUTING guidelines.


See Also

Getting Started:

  • Usage Guide - API conventions and how to navigate the documentation
  • Tutorial - Best practices and patterns for writing great tests
  • Examples - Practical code examples for common testing scenarios

Advanced Topics:

Reference:


  • Index Of Assertion Domains.

    Find the assertion function you need for your data.

  • Guides, examples and tutorials.
Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Subsections of Testify v2

Assertions index

The v2 our tests wanted

The testify/v2 package has a fairly large API surface.

This is in part inherited from the original testify and also largely due to the many available variants of the same assertion function.

The following sections organize the assertion API into usage domains, with all documented exported variants documented in a more concise form than the reference godoc documentation.

GoDoc

Domains

The testify API is organized in 19 logical domains shown below. Each domain contains assertions regrouped by their use case (e.g. http, json, error).

  • Expressing Assertions Using Conditions
  • Asserting OS Files
  • Asserting HTTP Response And Body
  • Asserting JSON Documents
  • Asserting How Collections Are Ordered
  • Asserting A Panic Behavior
  • Checks Against Leaked Resources (Goroutines, File Descriptors)
  • Mimics Methods From The Testing Standard Library
  • Asserting Times And Durations
  • Asserting Types Rather Than Values
  • Asserting Yaml Documents
  • Other Uncategorized Helpers

  • Boolean - Asserting Boolean Values (4)
  • Collection - Asserting Slices And Maps (19)
  • Comparison - Comparing Ordered Values (12)
  • Condition - Expressing Assertions Using Conditions (5)
  • Equality - Asserting Two Things Are Equal (16)
  • Error - Asserting Errors (8)
  • File - Asserting OS Files (6)
  • Http - Asserting HTTP Response And Body (7)
  • Json - Asserting JSON Documents (5)
  • Number - Asserting Numbers (7)
  • Ordering - Asserting How Collections Are Ordered (10)
  • Panic - Asserting A Panic Behavior (4)
  • Safety - Checks Against Leaked Resources (Goroutines, File Descriptors) (2)
  • String - Asserting Strings (4)
  • Testing - Mimics Methods From The Testing Standard Library (2)
  • Time - Asserting Times And Durations (2)
  • Type - Asserting Types Rather Than Values (10)
  • Yaml - Asserting Yaml Documents (5)
  • Common - Other Uncategorized Helpers (3)

Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Subsections of Assertions index

Boolean

Asserting Boolean Values

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 4 functionalities. Generic assertions are marked with a .

False

False asserts that the specified value is false.

Examples
	assertions.False(t, myBool)
	success: 1 == 0
	failure: 1 == 1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFalse(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFalse(t *testing.T)
    	success := assert.False(t, 1 == 0)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFalse(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFalse(t *testing.T)
    	require.False(t, 1 == 0)
    	fmt.Println("passed")
    
    }

FalseT[B Boolean]

FalseT asserts that the specified value is false.

The type constraint Boolean accepts any type which underlying type is bool.

Examples
	type B bool
	var b B = true
	assertions.FalseT(t, b)
	success: 1 == 0
	failure: 1 == 1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFalseT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFalseT(t *testing.T)
    	success := assert.FalseT(t, 1 == 0)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFalseT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFalseT(t *testing.T)
    	require.FalseT(t, 1 == 0)
    	fmt.Println("passed")
    
    }

True

True asserts that the specified value is true.

Examples
	assertions.True(t, myBool)
	success: 1 == 1
	failure: 1 == 0
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestTrue(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestTrue(t *testing.T)
    	success := assert.True(t, 1 == 1)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestTrue(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestTrue(t *testing.T)
    	require.True(t, 1 == 1)
    	fmt.Println("passed")
    
    }

TrueT[B Boolean]

TrueT asserts that the specified value is true.

The type constraint Boolean accepts any type which underlying type is bool.

Examples
	type B bool
	var b B = true
	assertions.True(t, b)
	success: 1 == 1
	failure: 1 == 0
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestTrueT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestTrueT(t *testing.T)
    	success := assert.TrueT(t, 1 == 1)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestTrueT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestTrueT(t *testing.T)
    	require.TrueT(t, 1 == 1)
    	fmt.Println("passed")
    
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Collection

Asserting Slices And Maps

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 19 functionalities. Generic assertions are marked with a .

Contains

Contains asserts that the specified string, list(array, slice…) or map contains the specified substring or element.

Examples
	assertions.Contains(t, "Hello World", "World")
	assertions.Contains(t, []string{"Hello", "World"}, "World")
	assertions.Contains(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"Hello": "World"}, "Hello")
	success: []string{"A","B"}, "A"
	failure: []string{"A","B"}, "C"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestContains(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestContains(t *testing.T)
    	success := assert.Contains(t, []string{"A", "B"}, "A")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestContains(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestContains(t *testing.T)
    	require.Contains(t, []string{"A", "B"}, "A")
    	fmt.Println("passed")
    
    }

ElementsMatch

ElementsMatch asserts that the specified listA(array, slice…) is equal to specified listB(array, slice…) ignoring the order of the elements. If there are duplicate elements, the number of appearances of each of them in both lists should match.

Examples
	assertions.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2})
	success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2}
	failure: []int{1, 2, 3}, []int{1, 2, 4}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestElementsMatch(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestElementsMatch(t *testing.T)
    	success := assert.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestElementsMatch(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestElementsMatch(t *testing.T)
    	require.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2})
    	fmt.Println("passed")
    
    }

ElementsMatchT[E comparable]

ElementsMatchT asserts that the specified listA(array, slice…) is equal to specified listB(array, slice…) ignoring the order of the elements. If there are duplicate elements, the number of appearances of each of them in both lists should match.

Examples
	assertions.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2})
	success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2}
	failure: []int{1, 2, 3}, []int{1, 2, 4}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestElementsMatchT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestElementsMatchT(t *testing.T)
    	success := assert.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestElementsMatchT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestElementsMatchT(t *testing.T)
    	require.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2})
    	fmt.Println("passed")
    
    }

Len

Len asserts that the specified object has specific length.

Len also fails if the object has a type that len() does not accept.

The asserted object can be a string, a slice, a map, an array, pointer to array or a channel.

Examples
	assertions.Len(t, mySlice, 3)
	assertions.Len(t, myString, 4)
	assertions.Len(t, myMap, 5)
	success: []string{"A","B"}, 2
	failure: []string{"A","B"}, 1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestLen(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestLen(t *testing.T)
    	success := assert.Len(t, []string{"A", "B"}, 2)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestLen(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestLen(t *testing.T)
    	require.Len(t, []string{"A", "B"}, 2)
    	fmt.Println("passed")
    
    }

MapContainsT[Map ~map[K]V, K comparable, V any]

MapContainsT asserts that the specified map contains a key.

Go native comparable types are explained there: comparable-types.

Examples
	assertions.MapContainsT(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"Hello": "x","World": "y"}, "World")
	success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "A"
	failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "C"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestMapContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestMapContainsT(t *testing.T)
    	success := assert.MapContainsT(t, map[string]string{"A": "B"}, "A")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestMapContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestMapContainsT(t *testing.T)
    	require.MapContainsT(t, map[string]string{"A": "B"}, "A")
    	fmt.Println("passed")
    
    }

MapNotContainsT[Map ~map[K]V, K comparable, V any]

MapNotContainsT asserts that the specified map does not contain a key.

Examples
	assertions.MapNotContainsT(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"Hello": "x","World": "y"}, "hi")
	success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "C"
	failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "A"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestMapNotContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestMapNotContainsT(t *testing.T)
    	success := assert.MapNotContainsT(t, map[string]string{"A": "B"}, "C")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestMapNotContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestMapNotContainsT(t *testing.T)
    	require.MapNotContainsT(t, map[string]string{"A": "B"}, "C")
    	fmt.Println("passed")
    
    }

NotContains

NotContains asserts that the specified string, list(array, slice…) or map does NOT contain the specified substring or element.

Examples
	assertions.NotContains(t, "Hello World", "Earth")
	assertions.NotContains(t, ["Hello", "World"], "Earth")
	assertions.NotContains(t, {"Hello": "World"}, "Earth")
	success: []string{"A","B"}, "C"
	failure: []string{"A","B"}, "B"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotContains(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotContains(t *testing.T)
    	success := assert.NotContains(t, []string{"A", "B"}, "C")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotContains(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotContains(t *testing.T)
    	require.NotContains(t, []string{"A", "B"}, "C")
    	fmt.Println("passed")
    
    }

NotElementsMatch

NotElementsMatch asserts that the specified listA(array, slice…) is NOT equal to specified listB(array, slice…) ignoring the order of the elements. If there are duplicate elements, the number of appearances of each of them in both lists should not match. This is an inverse of ElementsMatch.

Examples
	assertions.NotElementsMatch(t, []int{1, 1, 2, 3}, []int{1, 1, 2, 3}) -> false
	assertions.NotElementsMatch(t, []int{1, 1, 2, 3}, []int{1, 2, 3}) -> true
	assertions.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) -> true
	success: []int{1, 2, 3}, []int{1, 2, 4}
	failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotElementsMatch(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatch(t *testing.T)
    	success := assert.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotElementsMatch(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatch(t *testing.T)
    	require.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4})
    	fmt.Println("passed")
    
    }

NotElementsMatchT[E comparable]

NotElementsMatchT asserts that the specified listA(array, slice…) is NOT equal to specified listB(array, slice…) ignoring the order of the elements. If there are duplicate elements, the number of appearances of each of them in both lists should not match. This is an inverse of ElementsMatch.

Examples
	assertions.NotElementsMatchT(t, []int{1, 1, 2, 3}, []int{1, 1, 2, 3}) -> false
	assertions.NotElementsMatchT(t, []int{1, 1, 2, 3}, []int{1, 2, 3}) -> true
	assertions.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) -> true
	success: []int{1, 2, 3}, []int{1, 2, 4}
	failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotElementsMatchT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatchT(t *testing.T)
    	success := assert.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotElementsMatchT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatchT(t *testing.T)
    	require.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4})
    	fmt.Println("passed")
    
    }

NotSubset

NotSubset asserts that the list (array, slice, or map) does NOT contain all elements given in the subset (array, slice, or map). Map elements are key-value pairs unless compared with an array or slice where only the map key is evaluated.

Examples
	assertions.NotSubset(t, [1, 3, 4], [1, 2])
	assertions.NotSubset(t, {"x": 1, "y": 2}, {"z": 3})
	assertions.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"})
	assertions.NotSubset(t, {"x": 1, "y": 2}, ["z"])
	success: []int{1, 2, 3}, []int{4, 5}
	failure: []int{1, 2, 3}, []int{1, 2}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotSubset(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotSubset(t *testing.T)
    	success := assert.NotSubset(t, []int{1, 2, 3}, []int{4, 5})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotSubset(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotSubset(t *testing.T)
    	require.NotSubset(t, []int{1, 2, 3}, []int{4, 5})
    	fmt.Println("passed")
    
    }

SeqContainsT[E comparable]

SeqContainsT asserts that the specified iterator contains a comparable element.

The sequence may not be consumed entirely: the iteration stops as soon as the specified element is found.

Go native comparable types are explained there: comparable-types.

Examples
	assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World")
	success: slices.Values([]string{"A","B"}), "A"
	failure: slices.Values([]string{"A","B"}), "C"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSeqContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"slices"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSeqContainsT(t *testing.T)
    	success := assert.SeqContainsT(t, slices.Values([]string{"A", "B"}), "A")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSeqContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"slices"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSeqContainsT(t *testing.T)
    	require.SeqContainsT(t, slices.Values([]string{"A", "B"}), "A")
    	fmt.Println("passed")
    
    }

SeqNotContainsT[E comparable]

SeqNotContainsT asserts that the specified iterator does not contain a comparable element.

See SeqContainsT.

Examples
	assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World")
	success: slices.Values([]string{"A","B"}), "C"
	failure: slices.Values([]string{"A","B"}), "A"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSeqNotContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"slices"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSeqNotContainsT(t *testing.T)
    	success := assert.SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSeqNotContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"slices"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSeqNotContainsT(t *testing.T)
    	require.SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C")
    	fmt.Println("passed")
    
    }

SliceContainsT[Slice ~[]E, E comparable]

SliceContainsT asserts that the specified slice contains a comparable element.

Go native comparable types are explained there: comparable-types.

Examples
	assertions.SliceContainsT(t, []{"Hello","World"}, "World")
	success: []string{"A","B"}, "A"
	failure: []string{"A","B"}, "C"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSliceContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSliceContainsT(t *testing.T)
    	success := assert.SliceContainsT(t, []string{"A", "B"}, "A")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSliceContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSliceContainsT(t *testing.T)
    	require.SliceContainsT(t, []string{"A", "B"}, "A")
    	fmt.Println("passed")
    
    }

SliceNotContainsT[Slice ~[]E, E comparable]

SliceNotContainsT asserts that the specified slice does not contain a comparable element.

See SliceContainsT.

Examples
	assertions.SliceNotContainsT(t, []{"Hello","World"}, "hi")
	success: []string{"A","B"}, "C"
	failure: []string{"A","B"}, "A"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSliceNotContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSliceNotContainsT(t *testing.T)
    	success := assert.SliceNotContainsT(t, []string{"A", "B"}, "C")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSliceNotContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSliceNotContainsT(t *testing.T)
    	require.SliceNotContainsT(t, []string{"A", "B"}, "C")
    	fmt.Println("passed")
    
    }

SliceNotSubsetT[Slice ~[]E, E comparable]

SliceNotSubsetT asserts that a slice of comparable elements does not contain all the elements given in the subset.

Examples
	assertions.SliceNotSubsetT(t, []int{1, 2, 3}, []int{1, 4})
	success: []int{1, 2, 3}, []int{4, 5}
	failure: []int{1, 2, 3}, []int{1, 2}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSliceNotSubsetT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSliceNotSubsetT(t *testing.T)
    	success := assert.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSliceNotSubsetT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSliceNotSubsetT(t *testing.T)
    	require.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5})
    	fmt.Println("passed")
    
    }

SliceSubsetT[Slice ~[]E, E comparable]

SliceSubsetT asserts that a slice of comparable elements contains all the elements given in the subset.

Examples
	assertions.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2})
	success: []int{1, 2, 3}, []int{1, 2}
	failure: []int{1, 2, 3}, []int{4, 5}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSliceSubsetT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSliceSubsetT(t *testing.T)
    	success := assert.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSliceSubsetT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSliceSubsetT(t *testing.T)
    	require.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2})
    	fmt.Println("passed")
    
    }

StringContainsT[ADoc, EDoc Text]

StringContainsT asserts that a string contains the specified substring.

Strings may be go strings or []byte according to the type constraint Text.

Examples
	assertions.StringContainsT(t, "Hello World", "World")
	success: "AB", "A"
	failure: "AB", "C"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestStringContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestStringContainsT(t *testing.T)
    	success := assert.StringContainsT(t, "AB", "A")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestStringContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestStringContainsT(t *testing.T)
    	require.StringContainsT(t, "AB", "A")
    	fmt.Println("passed")
    
    }

StringNotContainsT[ADoc, EDoc Text]

StringNotContainsT asserts that a string does not contain the specified substring.

See StringContainsT.

Examples
	assertions.StringNotContainsT(t, "Hello World", "hi")
	success: "AB", "C"
	failure: "AB", "A"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestStringNotContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestStringNotContainsT(t *testing.T)
    	success := assert.StringNotContainsT(t, "AB", "C")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestStringNotContainsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestStringNotContainsT(t *testing.T)
    	require.StringNotContainsT(t, "AB", "C")
    	fmt.Println("passed")
    
    }

Subset

Subset asserts that the list (array, slice, or map) contains all elements given in the subset (array, slice, or map).

Map elements are key-value pairs unless compared with an array or slice where only the map key is evaluated.

nil values are considered as empty sets.

Examples
	assertions.Subset(t, []int{1, 2, 3}, []int{1, 2})
	assertions.Subset(t, []string{"x": 1, "y": 2}, []string{"x": 1})
	assertions.Subset(t, []int{1, 2, 3}, map[int](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#int)string{1: "one", 2: "two"})
	assertions.Subset(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)int{"x": 1, "y": 2}, []string{"x"})
	success: []int{1, 2, 3}, []int{1, 2}
	failure: []int{1, 2, 3}, []int{4, 5}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSubset(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSubset(t *testing.T)
    	success := assert.Subset(t, []int{1, 2, 3}, []int{1, 2})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSubset(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSubset(t *testing.T)
    	require.Subset(t, []int{1, 2, 3}, []int{1, 2})
    	fmt.Println("passed")
    
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Comparison

Comparing Ordered Values

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 12 functionalities. Generic assertions are marked with a .

Greater

Greater asserts that the first element is strictly greater than the second.

Both elements must be of the same type in the reflect.Kind sense. To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand.

Examples
	assertions.Greater(t, 2, 1)
	assertions.Greater(t, float64(2), float64(1))
	assertions.Greater(t, "b", "a")
	success: 2, 1
	failure: 1, 2
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestGreater(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestGreater(t *testing.T)
    	success := assert.Greater(t, 2, 1)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestGreater(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestGreater(t *testing.T)
    	require.Greater(t, 2, 1)
    	fmt.Println("passed")
    
    }

GreaterOrEqual

GreaterOrEqual asserts that the first element is greater than or equal to the second.

See also Greater.

Examples
	assertions.GreaterOrEqual(t, 2, 1)
	assertions.GreaterOrEqual(t, 2, 2)
	assertions.GreaterOrEqual(t, "b", "a")
	assertions.GreaterOrEqual(t, "b", "b")
	success: 2, 1
	failure: 1, 2
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestGreaterOrEqual(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqual(t *testing.T)
    	success := assert.GreaterOrEqual(t, 2, 1)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestGreaterOrEqual(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqual(t *testing.T)
    	require.GreaterOrEqual(t, 2, 1)
    	fmt.Println("passed")
    
    }

GreaterOrEqualT[Orderable Ordered]

GreaterOrEqualT asserts that for two elements of the same type, the first element is greater than or equal to the second.

The Ordered type can be any of Go’s cmp.Ordered (strings, numeric types), []byte (uses bytes.Compare) and time.Time (uses time.Time.Compare.

Notice that pointers are not Ordered, but uintptr are. So you can’t call GreaterOrEqualT with [*time.Time].

GreaterOrEqualT ensures type safety at build time. If you need to compare values with a dynamically assigned type, use GreaterOrEqual instead.

To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand.

Examples
	assertions.GreaterOrEqualT(t, 2, 1)
	assertions.GreaterOrEqualT(t, 2, 2)
	assertions.GreaterOrEqualT(t, "b", "a")
	assertions.GreaterOrEqualT(t, "b", "b")
	success: 2, 1
	failure: 1, 2
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestGreaterOrEqualT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqualT(t *testing.T)
    	success := assert.GreaterOrEqualT(t, 2, 1)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestGreaterOrEqualT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqualT(t *testing.T)
    	require.GreaterOrEqualT(t, 2, 1)
    	fmt.Println("passed")
    
    }

GreaterT[Orderable Ordered]

GreaterT asserts that for two elements of the same type, the first element is strictly greater than the second.

The Ordered type can be any of Go’s cmp.Ordered (strings, numeric types), []byte (uses bytes.Compare) and time.Time (uses time.Time.Compare.

Notice that pointers are not Ordered, but uintptr are. So you can’t call GreaterT with [*time.Time].

GreaterT ensures type safety at build time. If you need to compare values with a dynamically assigned type, use Greater instead.

To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand.

Examples
	assertions.GreaterT(t, 2, 1)
	assertions.GreaterT(t, float64(2), float64(1))
	assertions.GreaterT(t, "b", "a")
	assertions.GreaterT(t, time.Date(2026,1,1,0,0,0,0,nil), time.Now())
	success: 2, 1
	failure: 1, 2
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestGreaterT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestGreaterT(t *testing.T)
    	success := assert.GreaterT(t, 2, 1)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestGreaterT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestGreaterT(t *testing.T)
    	require.GreaterT(t, 2, 1)
    	fmt.Println("passed")
    
    }

Less

Less asserts that the first element is strictly less than the second.

Both elements must be of the same type in the reflect.Kind sense. To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand.

Examples
	assertions.Less(t, 1, 2)
	assertions.Less(t, float64(1), float64(2))
	assertions.Less(t, "a", "b")
	success: 1, 2
	failure: 2, 1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestLess(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestLess(t *testing.T)
    	success := assert.Less(t, 1, 2)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestLess(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestLess(t *testing.T)
    	require.Less(t, 1, 2)
    	fmt.Println("passed")
    
    }

LessOrEqual

LessOrEqual asserts that the first element is less than or equal to the second.

Examples
	assertions.LessOrEqual(t, 1, 2)
	assertions.LessOrEqual(t, 2, 2)
	assertions.LessOrEqual(t, "a", "b")
	assertions.LessOrEqual(t, "b", "b")
	success: 1, 2
	failure: 2, 1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestLessOrEqual(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestLessOrEqual(t *testing.T)
    	success := assert.LessOrEqual(t, 1, 2)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestLessOrEqual(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestLessOrEqual(t *testing.T)
    	require.LessOrEqual(t, 1, 2)
    	fmt.Println("passed")
    
    }

LessOrEqualT[Orderable Ordered]

LessOrEqualT asserts that for two elements of the same type, the first element is less than or equal to the second.

The Ordered type can be any of Go’s cmp.Ordered (strings, numeric types), []byte (uses bytes.Compare) and time.Time (uses time.Time.Compare.

Notice that pointers are not Ordered, but uintptr are. So you can’t call LessOrEqualT with [*time.Time].

LessOrEqualT ensures type safety at build time. If you need to compare values with a dynamically assigned type, use LessOrEqual instead.

To compare values that need a type conversion (e.g. float32 against float64), you should use LessOrEqual instead.

Examples
	assertions.LessOrEqualT(t, 1, 2)
	assertions.LessOrEqualT(t, 2, 2)
	assertions.LessOrEqualT(t, "a", "b")
	assertions.LessOrEqualT(t, "b", "b")
	success: 1, 2
	failure: 2, 1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestLessOrEqualT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestLessOrEqualT(t *testing.T)
    	success := assert.LessOrEqualT(t, 1, 2)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestLessOrEqualT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestLessOrEqualT(t *testing.T)
    	require.LessOrEqualT(t, 1, 2)
    	fmt.Println("passed")
    
    }

LessT[Orderable Ordered]

LessT asserts that for two elements of the same type, the first element is strictly less than the second.

The Ordered type can be any of Go’s cmp.Ordered (strings, numeric types), []byte (uses bytes.Compare) and time.Time (uses time.Time.Compare.

Notice that pointers are not Ordered, but uintptr are. So you can’t call LessT with [*time.Time].

LessT ensures type safety at build time. If you need to compare values with a dynamically assigned type, use Less instead.

To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand.

Examples
	assertions.LessT(t, 1, 2)
	assertions.LessT(t, float64(1), float64(2))
	assertions.LessT(t, "a", "b")
	success: 1, 2
	failure: 2, 1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestLessT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestLessT(t *testing.T)
    	success := assert.LessT(t, 1, 2)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestLessT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestLessT(t *testing.T)
    	require.LessT(t, 1, 2)
    	fmt.Println("passed")
    
    }

Negative

Negative asserts that the specified element is strictly negative.

Examples
	assertions.Negative(t, -1)
	assertions.Negative(t, -1.23)
	success: -1
	failure: 1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNegative(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNegative(t *testing.T)
    	success := assert.Negative(t, -1)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNegative(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNegative(t *testing.T)
    	require.Negative(t, -1)
    	fmt.Println("passed")
    
    }

NegativeT[SignedNumber SignedNumeric]

NegativeT asserts that the specified element of a signed numeric type is strictly negative.

Examples
	assertions.NegativeT(t, -1)
	assertions.NegativeT(t, -1.23)
	success: -1
	failure: 1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNegativeT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNegativeT(t *testing.T)
    	success := assert.NegativeT(t, -1)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNegativeT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNegativeT(t *testing.T)
    	require.NegativeT(t, -1)
    	fmt.Println("passed")
    
    }

Positive

Positive asserts that the specified element is strictly positive.

Examples
	assertions.Positive(t, 1)
	assertions.Positive(t, 1.23)
	success: 1
	failure: -1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestPositive(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestPositive(t *testing.T)
    	success := assert.Positive(t, 1)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestPositive(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestPositive(t *testing.T)
    	require.Positive(t, 1)
    	fmt.Println("passed")
    
    }

PositiveT[SignedNumber SignedNumeric]

PositiveT asserts that the specified element of a signed numeric type is strictly positive.

Examples
	assertions.PositiveT(t, 1)
	assertions.PositiveT(t, 1.23)
	success: 1
	failure: -1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestPositiveT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestPositiveT(t *testing.T)
    	success := assert.PositiveT(t, 1)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestPositiveT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestPositiveT(t *testing.T)
    	require.PositiveT(t, 1)
    	fmt.Println("passed")
    
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Condition

Expressing Assertions Using Conditions

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 5 functionalities. Generic assertions are marked with a .

Condition

Condition uses a comparison function to assert a complex condition.

Examples
	assertions.Condition(t, func() bool { return myCondition })
	success:  func() bool { return true }
	failure:  func() bool { return false }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestCondition(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestCondition(t *testing.T)
    	success := assert.Condition(t, func() bool {
    		return true
    	})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestCondition(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestCondition(t *testing.T)
    	require.Condition(t, func() bool {
    		return true
    	})
    	fmt.Println("passed")
    
    }

Consistently[C Conditioner]

Consistently asserts that the given condition is always satisfied until timeout, periodically checking the target function at each tick.

Consistently (“always”) imposes a stronger constraint than Eventually (“at least once”): it checks at every tick that every occurrence of the condition is satisfied, whereas Eventually succeeds on the first occurrence of a successful condition.

Alternative condition signature

The simplest form of condition is:

func() bool

The semantics of the assertion are “always returns true”.

To build more complex cases, a condition may also be defined as:

func(context.Context) error

It fails as soon as an error is returned before timeout expressing “always returns no error (nil)”

This is consistent with Eventually expressing “eventually returns no error (nil)”.

It will be executed with the context of the assertion, which inherits the testing.T.Context and is cancelled on timeout.

Concurrency

See Eventually.

Attention point

See Eventually.

Examples
	assertions.Consistently(t, func() bool { return true }, time.Second, 10*time.Millisecond)
See also [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) for details about using context and concurrency.
	success:  func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond
	failure:  func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestConsistently(t *testing.T)
    	success := assert.Consistently(t, func() bool {
    		return true
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"context"
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate a service that stays healthy.
    	healthCheck := func(_ context.Context) error {
    		return nil // always healthy
    	}
    
    	result := assert.Consistently(t, healthCheck, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("consistently healthy: %t", result)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A counter that stays within bounds during the test.
    	var counter atomic.Int32
    	counter.Store(5)
    
    	result := assert.Consistently(t, func() bool {
    		return counter.Load() < 10
    	}, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("consistently under limit: %t", result)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestConsistently(t *testing.T)
    	require.Consistently(t, func() bool {
    		return true
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Println("passed")
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"context"
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate a service that stays healthy.
    	healthCheck := func(_ context.Context) error {
    		return nil // always healthy
    	}
    
    	require.Consistently(t, healthCheck, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("consistently healthy: %t", !t.Failed())
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A counter that stays within bounds during the test.
    	var counter atomic.Int32
    	counter.Store(5)
    
    	require.Consistently(t, func() bool {
    		return counter.Load() < 10
    	}, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("consistently under limit: %t", !t.Failed())
    
    }

Eventually[C Conditioner]

Eventually asserts that the given condition will be met before timeout, periodically checking the target function on each tick.

Eventually waits until the condition returns true, at most until timeout, or until the parent context of the test is cancelled.

If the condition takes longer than the timeout to complete, Eventually fails but waits for the current condition execution to finish before returning.

For long-running conditions to be interrupted early, check testing.T.Context which is cancelled on test failure.

Alternative condition signature

The simplest form of condition is:

func() bool

To build more complex cases, a condition may also be defined as:

func(context.Context) error

It fails when an error has always been returned up to timeout (equivalent semantics to func() bool returns false), expressing “eventually returns no error (nil)”.

It will be executed with the context of the assertion, which inherits the testing.T.Context and is cancelled on timeout.

The semantics of the three available async assertions read as follows.

  • Eventually (func() bool) : “eventually returns true”

  • Never (func() bool) : “never returns true”

  • Consistently (func() bool): “always returns true”

  • Eventually (func(ctx) error) : “eventually returns nil”

  • Never (func(ctx) error) : not supported, use Consistently instead (avoids confusion with double negation)

  • Consistently (func(ctx) error): “always returns nil”

Concurrency

The condition function is always executed serially by a single goroutine. It is always executed at least once.

It may thus write to variables outside its scope without triggering race conditions.

A blocking condition will cause Eventually to hang until it returns.

Notice that time ticks may be skipped if the condition takes longer than the tick interval.

Attention point

Time-based tests may be flaky in a resource-constrained environment such as a CI runner and may produce counter-intuitive results, such as ticks or timeouts not firing in time as expected.

To avoid flaky tests, always make sure that ticks and timeouts differ by at least an order of magnitude (tick « timeout).

Examples
	assertions.Eventually(t, func() bool { return true }, time.Second, 10*time.Millisecond)
	success:  func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond
	failure:  func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEventually(t *testing.T)
    	success := assert.Eventually(t, func() bool {
    		return true
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate an async operation that completes after a short delay.
    	var ready atomic.Bool
    	go func() {
    		time.Sleep(30 * time.Millisecond)
    		ready.Store(true)
    	}()
    
    	result := assert.Eventually(t, ready.Load, 200*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("eventually ready: %t", result)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"context"
    	"errors"
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate a service that becomes healthy after a few attempts.
    	var attempts atomic.Int32
    	healthCheck := func(_ context.Context) error {
    		if attempts.Add(1) < 3 {
    			return errors.New("service not ready")
    		}
    
    		return nil
    	}
    
    	result := assert.Eventually(t, healthCheck, 200*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("eventually healthy: %t", result)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEventually(t *testing.T)
    	require.Eventually(t, func() bool {
    		return true
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Println("passed")
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate an async operation that completes after a short delay.
    	var ready atomic.Bool
    	go func() {
    		time.Sleep(30 * time.Millisecond)
    		ready.Store(true)
    	}()
    
    	require.Eventually(t, ready.Load, 200*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("eventually ready: %t", !t.Failed())
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"context"
    	"errors"
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate a service that becomes healthy after a few attempts.
    	var attempts atomic.Int32
    	healthCheck := func(_ context.Context) error {
    		if attempts.Add(1) < 3 {
    			return errors.New("service not ready")
    		}
    
    		return nil
    	}
    
    	require.Eventually(t, healthCheck, 200*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("eventually healthy: %t", !t.Failed())
    
    }

EventuallyWith[C CollectibleConditioner]

EventuallyWith asserts that the given condition will be met before the timeout, periodically checking the target function at each tick.

In contrast to Eventually, the condition function is supplied with a CollectT to accumulate errors from calling other assertions.

The condition is considered “met” if no errors are raised in a tick. The supplied CollectT collects all errors from one tick.

If the condition is not met before the timeout, the collected errors from the last tick are copied to t.

Calling CollectT.FailNow cancels the condition immediately and causes the assertion to fail.

Concurrency

The condition function is never executed in parallel: only one goroutine executes it. It may write to variables outside its scope without triggering race conditions.

Examples
	externalValue := false
	go func() {
		time.Sleep(8*time.Second)
		externalValue = true
	}()
	assertions.EventuallyWith(t, func(c *assertions.CollectT) {
		// add assertions as needed; any assertion failure will fail the current tick
		assertions.True(c, externalValue, "expected 'externalValue' to be true")
	},
	10*time.Second,
	1*time.Second,
	"external state has not changed to 'true'; still false",
	)
	success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond
	failure: func(c *CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventuallyWith(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEventuallyWith(t *testing.T)
    	success := assert.EventuallyWith(t, func(c *assert.CollectT) {
    		assert.True(c, true)
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventuallyWith(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEventuallyWith(t *testing.T)
    	require.EventuallyWith(t, func(c *assert.CollectT) {
    		assert.True(c, true)
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Println("passed")
    
    }

Never

Never asserts that the given condition is never satisfied until timeout, periodically checking the target function at each tick.

Never is the opposite of Eventually (“at least once”). It succeeds if the timeout is reached without the condition ever returning true.

If the parent context is cancelled before the timeout, Never fails.

Alternative condition signature

The simplest form of condition is:

func() bool

Use Consistently instead if you want to use a condition returning an error.

Concurrency

See Eventually.

Attention point

See Eventually.

Examples
	assertions.Never(t, func() bool { return false }, time.Second, 10*time.Millisecond)
See also [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) for details about using context and concurrency.
	success:  func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond
	failure:  func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNever(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNever(t *testing.T)
    	success := assert.Never(t, func() bool {
    		return false
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNever(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A channel that should remain empty during the test.
    	events := make(chan struct{}, 1)
    
    	result := assert.Never(t, func() bool {
    		select {
    		case <-events:
    			return true // event received = condition becomes true = Never fails
    		default:
    			return false
    		}
    	}, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("never received: %t", result)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNever(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNever(t *testing.T)
    	require.Never(t, func() bool {
    		return false
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Println("passed")
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNever(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A channel that should remain empty during the test.
    	events := make(chan struct{}, 1)
    
    	require.Never(t, func() bool {
    		select {
    		case <-events:
    			return true // event received = condition becomes true = Never fails
    		default:
    			return false
    		}
    	}, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("never received: %t", !t.Failed())
    
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Equality

Asserting Two Things Are Equal

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 16 functionalities. Generic assertions are marked with a .

Empty

Empty asserts that the given value is “empty”.

Zero values are “empty”.

Arrays are “empty” if every element is the zero value of the type (stricter than “empty”).

Slices, maps and channels with zero length are “empty”.

Pointer values are “empty” if the pointer is nil or if the pointed value is “empty”.

Examples
	assertions.Empty(t, obj)
	success: ""
	failure: "not empty"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEmpty(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEmpty(t *testing.T)
    	success := assert.Empty(t, "")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEmpty(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEmpty(t *testing.T)
    	require.Empty(t, "")
    	fmt.Println("passed")
    
    }

Equal

Equal asserts that two objects are equal.

Pointer variable equality is determined based on the equality of the referenced values (as opposed to the memory addresses).

Function equality cannot be determined and will always fail.

Examples
	assertions.Equal(t, 123, 123)
	success: 123, 123
	failure: 123, 456
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEqual(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEqual(t *testing.T)
    	success := assert.Equal(t, 123, 123)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEqual(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEqual(t *testing.T)
    	require.Equal(t, 123, 123)
    	fmt.Println("passed")
    
    }

EqualExportedValues

EqualExportedValues asserts that the types of two objects are equal and their public fields are also equal.

This is useful for comparing structs that have private fields that could potentially differ.

Function equality cannot be determined and will always fail.

Examples
	type S struct {
		Exported     	int
		notExported   	int
	}
	assertions.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true
	assertions.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false
	success: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2}
	failure: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "b", b: 1}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEqualExportedValues(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEqualExportedValues(t *testing.T)
    	success := assert.EqualExportedValues(t, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2})
    	fmt.Printf("success: %t\n", success)
    
    }
    
    type dummyStruct struct {
    	A string
    	b int
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEqualExportedValues(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEqualExportedValues(t *testing.T)
    	require.EqualExportedValues(t, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2})
    	fmt.Println("passed")
    
    }
    
    type dummyStruct struct {
    	A string
    	b int
    }

EqualT[V comparable]

EqualT asserts that two objects of the same comparable type are equal.

Pointer variable equality is determined based on the equality of the memory addresses (unlike Equal, but like Same).

Functions, slices and maps are not comparable. See also ComparisonOperators.

If you need to compare values of non-comparable types, or compare pointers by the value they point to, use Equal instead.

Examples
	assertions.EqualT(t, 123, 123)
	success: 123, 123
	failure: 123, 456
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEqualT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEqualT(t *testing.T)
    	success := assert.EqualT(t, 123, 123)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEqualT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEqualT(t *testing.T)
    	require.EqualT(t, 123, 123)
    	fmt.Println("passed")
    
    }

EqualValues

EqualValues asserts that two objects are equal or convertible to the larger type and equal.

Function equality cannot be determined and will always fail.

Examples
	assertions.EqualValues(t, uint32(123), int32(123))
	success: uint32(123), int32(123)
	failure: uint32(123), int32(456)
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEqualValues(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEqualValues(t *testing.T)
    	success := assert.EqualValues(t, uint32(123), int32(123))
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEqualValues(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEqualValues(t *testing.T)
    	require.EqualValues(t, uint32(123), int32(123))
    	fmt.Println("passed")
    
    }

Exactly

Exactly asserts that two objects are equal in value and type.

Examples
	assertions.Exactly(t, int32(123), int64(123))
	success: int32(123), int32(123)
	failure: int32(123), int64(123)
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestExactly(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestExactly(t *testing.T)
    	success := assert.Exactly(t, int32(123), int32(123))
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestExactly(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestExactly(t *testing.T)
    	require.Exactly(t, int32(123), int32(123))
    	fmt.Println("passed")
    
    }

Nil

Nil asserts that the specified object is nil.

Examples
	assertions.Nil(t, err)
	success: nil
	failure: "not nil"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNil(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNil(t *testing.T)
    	success := assert.Nil(t, nil)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNil(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNil(t *testing.T)
    	require.Nil(t, nil)
    	fmt.Println("passed")
    
    }

NotEmpty

NotEmpty asserts that the specified object is NOT Empty.

Examples
	if assert.NotEmpty(t, obj) {
		assertions.Equal(t, "two", obj[1](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#1))
	}
	success: "not empty"
	failure: ""
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotEmpty(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotEmpty(t *testing.T)
    	success := assert.NotEmpty(t, "not empty")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotEmpty(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotEmpty(t *testing.T)
    	require.NotEmpty(t, "not empty")
    	fmt.Println("passed")
    
    }

NotEqual

NotEqual asserts that the specified values are NOT equal.

Examples
	assertions.NotEqual(t, obj1, obj2)
Pointer variable equality is determined based on the equality of the
referenced values (as opposed to the memory addresses).
Function equality cannot be determined and will always fail.
	success: 123, 456
	failure: 123, 123
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotEqual(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotEqual(t *testing.T)
    	success := assert.NotEqual(t, 123, 456)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotEqual(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotEqual(t *testing.T)
    	require.NotEqual(t, 123, 456)
    	fmt.Println("passed")
    
    }

NotEqualT[V comparable]

NotEqualT asserts that the specified values of the same comparable type are NOT equal.

See EqualT.

Examples
	assertions.NotEqualT(t, obj1, obj2)
	success: 123, 456
	failure: 123, 123
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotEqualT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotEqualT(t *testing.T)
    	success := assert.NotEqualT(t, 123, 456)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotEqualT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotEqualT(t *testing.T)
    	require.NotEqualT(t, 123, 456)
    	fmt.Println("passed")
    
    }

NotEqualValues

NotEqualValues asserts that two objects are not equal even when converted to the same type.

Function equality cannot be determined and will always fail.

Examples
	assertions.NotEqualValues(t, obj1, obj2)
	success: uint32(123), int32(456)
	failure: uint32(123), int32(123)
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotEqualValues(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotEqualValues(t *testing.T)
    	success := assert.NotEqualValues(t, uint32(123), int32(456))
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotEqualValues(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotEqualValues(t *testing.T)
    	require.NotEqualValues(t, uint32(123), int32(456))
    	fmt.Println("passed")
    
    }

NotNil

NotNil asserts that the specified object is not nil.

Examples
	assertions.NotNil(t, err)
	success: "not nil"
	failure: nil
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotNil(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotNil(t *testing.T)
    	success := assert.NotNil(t, "not nil")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotNil(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotNil(t *testing.T)
    	require.NotNil(t, "not nil")
    	fmt.Println("passed")
    
    }

NotSame

NotSame asserts that two pointers do not reference the same object.

See Same.

Examples
	assertions.NotSame(t, ptr1, ptr2)
	success: &staticVar, ptr("static string")
	failure: &staticVar, staticVarPtr
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotSame(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotSame(t *testing.T)
    	success := assert.NotSame(t, &staticVar, ptr("static string"))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing
    var staticVar = "static string"
    
    func ptr[T any](value T) *T {
    	p := value
    
    	return &p
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotSame(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotSame(t *testing.T)
    	require.NotSame(t, &staticVar, ptr("static string"))
    	fmt.Println("passed")
    
    }
    
    //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing
    var staticVar = "static string"
    
    func ptr[T any](value T) *T {
    	p := value
    
    	return &p
    }

NotSameT[P any]

NotSameT asserts that two pointers do not reference the same object.

See SameT.

Examples
	assertions.NotSameT(t, ptr1, ptr2)
	success: &staticVar, ptr("static string")
	failure: &staticVar, staticVarPtr
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotSameT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotSameT(t *testing.T)
    	success := assert.NotSameT(t, &staticVar, ptr("static string"))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing
    var staticVar = "static string"
    
    func ptr[T any](value T) *T {
    	p := value
    
    	return &p
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotSameT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotSameT(t *testing.T)
    	require.NotSameT(t, &staticVar, ptr("static string"))
    	fmt.Println("passed")
    
    }
    
    //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing
    var staticVar = "static string"
    
    func ptr[T any](value T) *T {
    	p := value
    
    	return &p
    }

Same

Same asserts that two pointers reference the same object.

Both arguments must be pointer variables.

Pointer variable sameness is determined based on the equality of both type and value.

Unlike Equal pointers, Same pointers point to the same memory address.

Examples
	assertions.Same(t, ptr1, ptr2)
	success: &staticVar, staticVarPtr
	failure: &staticVar, ptr("static string")
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSame(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSame(t *testing.T)
    	success := assert.Same(t, &staticVar, staticVarPtr)
    	fmt.Printf("success: %t\n", success)
    
    }
    
    //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing
    var (
    	staticVar    = "static string"
    	staticVarPtr = &staticVar
    )
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSame(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSame(t *testing.T)
    	require.Same(t, &staticVar, staticVarPtr)
    	fmt.Println("passed")
    
    }
    
    //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing
    var (
    	staticVar    = "static string"
    	staticVarPtr = &staticVar
    )

SameT[P any]

SameT asserts that two pointers of the same type reference the same object.

See Same.

Examples
	assertions.SameT(t, ptr1, ptr2)
	success: &staticVar, staticVarPtr
	failure: &staticVar, ptr("static string")
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSameT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSameT(t *testing.T)
    	success := assert.SameT(t, &staticVar, staticVarPtr)
    	fmt.Printf("success: %t\n", success)
    
    }
    
    //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing
    var (
    	staticVar    = "static string"
    	staticVarPtr = &staticVar
    )
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSameT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSameT(t *testing.T)
    	require.SameT(t, &staticVar, staticVarPtr)
    	fmt.Println("passed")
    
    }
    
    //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing
    var (
    	staticVar    = "static string"
    	staticVarPtr = &staticVar
    )


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Error

Asserting Errors

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 8 functionalities.

EqualError

EqualError asserts that a function returned a non-nil error (i.e. an error) and that it is equal to the provided error.

Examples
	actualObj, err := SomeFunction()
	assertions.EqualError(t, err,  expectedErrorString)
	success: ErrTest, "assert.ErrTest general error for testing"
	failure: ErrTest, "wrong error message"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEqualError(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEqualError(t *testing.T)
    	success := assert.EqualError(t, assert.ErrTest, "assert.ErrTest general error for testing")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEqualError(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEqualError(t *testing.T)
    	require.EqualError(t, require.ErrTest, "assert.ErrTest general error for testing")
    	fmt.Println("passed")
    
    }

Error

Error asserts that a function returned a non-nil error (i.e. an error).

Examples
	actualObj, err := SomeFunction()
	assertions.Error(t, err)
	success: ErrTest
	failure: nil
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestError(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestError(t *testing.T)
    	success := assert.Error(t, assert.ErrTest)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestError(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestError(t *testing.T)
    	require.Error(t, require.ErrTest)
    	fmt.Println("passed")
    
    }

ErrorAs

ErrorAs asserts that at least one of the errors in err’s chain matches target, and if so, sets target to that error value.

This is a wrapper for errors.As.

Examples
	assertions.ErrorAs(t, err, &target)
	success: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)
	failure: ErrTest, new(*dummyError)
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestErrorAs(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestErrorAs(t *testing.T)
    	success := assert.ErrorAs(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    type dummyError struct {
    }
    
    func (d *dummyError) Error() string {
    	return "dummy error"
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestErrorAs(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestErrorAs(t *testing.T)
    	require.ErrorAs(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError))
    	fmt.Println("passed")
    
    }
    
    type dummyError struct {
    }
    
    func (d *dummyError) Error() string {
    	return "dummy error"
    }

ErrorContains

ErrorContains asserts that a function returned a non-nil error (i.e. an error) and that the error contains the specified substring.

Examples
	actualObj, err := SomeFunction()
	assertions.ErrorContains(t, err,  expectedErrorSubString)
	success: ErrTest, "general error"
	failure: ErrTest, "not in message"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestErrorContains(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestErrorContains(t *testing.T)
    	success := assert.ErrorContains(t, assert.ErrTest, "general error")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestErrorContains(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestErrorContains(t *testing.T)
    	require.ErrorContains(t, require.ErrTest, "general error")
    	fmt.Println("passed")
    
    }

ErrorIs

ErrorIs asserts that at least one of the errors in err’s chain matches target.

This is a wrapper for errors.Is.

Examples
	assertions.ErrorIs(t, err, io.EOF)
	success: fmt.Errorf("wrap: %w", io.EOF), io.EOF
	failure: ErrTest, io.EOF
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestErrorIs(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"io"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestErrorIs(t *testing.T)
    	success := assert.ErrorIs(t, fmt.Errorf("wrap: %w", io.EOF), io.EOF)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestErrorIs(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"io"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestErrorIs(t *testing.T)
    	require.ErrorIs(t, fmt.Errorf("wrap: %w", io.EOF), io.EOF)
    	fmt.Println("passed")
    
    }

NoError

NoError asserts that a function returned a nil error (i.e. no error).

Examples
	actualObj, err := SomeFunction()
	if assert.NoError(t, err) {
		assertions.Equal(t, expectedObj, actualObj)
	}
	success: nil
	failure: ErrTest
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoError(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNoError(t *testing.T)
    	success := assert.NoError(t, nil)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoError(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNoError(t *testing.T)
    	require.NoError(t, nil)
    	fmt.Println("passed")
    
    }

NotErrorAs

NotErrorAs asserts that none of the errors in err’s chain matches target, but if so, sets target to that error value.

Examples
	assertions.NotErrorAs(t, err, &target)
	success: ErrTest, new(*dummyError)
	failure: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotErrorAs(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotErrorAs(t *testing.T)
    	success := assert.NotErrorAs(t, assert.ErrTest, new(*dummyError))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    type dummyError struct {
    }
    
    func (d *dummyError) Error() string {
    	return "dummy error"
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotErrorAs(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotErrorAs(t *testing.T)
    	require.NotErrorAs(t, require.ErrTest, new(*dummyError))
    	fmt.Println("passed")
    
    }
    
    type dummyError struct {
    }
    
    func (d *dummyError) Error() string {
    	return "dummy error"
    }

NotErrorIs

NotErrorIs asserts that none of the errors in err’s chain matches target.

This is a wrapper for errors.Is.

Examples
	assertions.NotErrorIs(t, err, io.EOF)
	success: ErrTest, io.EOF
	failure: fmt.Errorf("wrap: %w", io.EOF), io.EOF
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotErrorIs(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"io"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotErrorIs(t *testing.T)
    	success := assert.NotErrorIs(t, assert.ErrTest, io.EOF)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotErrorIs(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"io"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotErrorIs(t *testing.T)
    	require.NotErrorIs(t, require.ErrTest, io.EOF)
    	fmt.Println("passed")
    
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

File

Asserting OS Files

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 6 functionalities.

DirExists

DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.

Examples
	assertions.DirExists(t, "path/to/directory")
	success: filepath.Join(testDataPath(),"existing_dir")
	failure: filepath.Join(testDataPath(),"non_existing_dir")
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestDirExists(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestDirExists(t *testing.T)
    	success := assert.DirExists(t, filepath.Join(testDataPath(), "existing_dir"))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestDirExists(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestDirExists(t *testing.T)
    	require.DirExists(t, filepath.Join(testDataPath(), "existing_dir"))
    	fmt.Println("passed")
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }

DirNotExists

DirNotExists checks whether a directory does not exist in the given path. It fails if the path points to an existing directory only.

Examples
	assertions.DirNotExists(t, "path/to/directory")
	success: filepath.Join(testDataPath(),"non_existing_dir")
	failure: filepath.Join(testDataPath(),"existing_dir")
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestDirNotExists(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestDirNotExists(t *testing.T)
    	success := assert.DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir"))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestDirNotExists(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestDirNotExists(t *testing.T)
    	require.DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir"))
    	fmt.Println("passed")
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }

FileEmpty

FileEmpty checks whether a file exists in the given path and is empty. It fails if the file is not empty, if the path points to a directory or there is an error when trying to check the file.

Examples
	assertions.FileEmpty(t, "path/to/file")
	success: filepath.Join(testDataPath(),"empty_file")
	failure: filepath.Join(testDataPath(),"existing_file")
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFileEmpty(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFileEmpty(t *testing.T)
    	success := assert.FileEmpty(t, filepath.Join(testDataPath(), "empty_file"))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFileEmpty(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFileEmpty(t *testing.T)
    	require.FileEmpty(t, filepath.Join(testDataPath(), "empty_file"))
    	fmt.Println("passed")
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }

FileExists

FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.

Examples
	assertions.FileExists(t, "path/to/file")
	success: filepath.Join(testDataPath(),"existing_file")
	failure: filepath.Join(testDataPath(),"non_existing_file")
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFileExists(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFileExists(t *testing.T)
    	success := assert.FileExists(t, filepath.Join(testDataPath(), "existing_file"))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFileExists(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFileExists(t *testing.T)
    	require.FileExists(t, filepath.Join(testDataPath(), "existing_file"))
    	fmt.Println("passed")
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }

FileNotEmpty

FileNotEmpty checks whether a file exists in the given path and is not empty. It fails if the file is empty, if the path points to a directory or there is an error when trying to check the file.

Examples
	assertions.FileNotEmpty(t, "path/to/file")
	success: filepath.Join(testDataPath(),"existing_file")
	failure: filepath.Join(testDataPath(),"empty_file")
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFileNotEmpty(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFileNotEmpty(t *testing.T)
    	success := assert.FileNotEmpty(t, filepath.Join(testDataPath(), "existing_file"))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFileNotEmpty(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFileNotEmpty(t *testing.T)
    	require.FileNotEmpty(t, filepath.Join(testDataPath(), "existing_file"))
    	fmt.Println("passed")
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }

FileNotExists

FileNotExists checks whether a file does not exist in a given path. It fails if the path points to an existing file only.

Examples
	assertions.FileNotExists(t, "path/to/file")
	success: filepath.Join(testDataPath(),"non_existing_file")
	failure: filepath.Join(testDataPath(),"existing_file")
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFileNotExists(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFileNotExists(t *testing.T)
    	success := assert.FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file"))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestFileNotExists(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"path/filepath"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestFileNotExists(t *testing.T)
    	require.FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file"))
    	fmt.Println("passed")
    
    }
    
    func testDataPath() string {
    	return filepath.Join("..", "internal", "assertions", "testdata")
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Http

Asserting HTTP Response And Body

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 7 functionalities.

HTTPBodyContains

HTTPBodyContains asserts that a specified handler returns a body that contains a string.

Returns whether the assertion was successful (true) or not (false).

Examples
	assertions.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
	success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"
	failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, World!"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPBodyContains(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"net/url"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyContains(t *testing.T)
    	success := assert.HTTPBodyContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func httpBody(w http.ResponseWriter, r *http.Request) {
    	name := r.FormValue("name")
    	_, _ = fmt.Fprintf(w, "Hello, %s!", name)
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPBodyContains(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"net/url"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyContains(t *testing.T)
    	require.HTTPBodyContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")
    	fmt.Println("passed")
    
    }
    
    func httpBody(w http.ResponseWriter, r *http.Request) {
    	name := r.FormValue("name")
    	_, _ = fmt.Fprintf(w, "Hello, %s!", name)
    }

HTTPBodyNotContains

HTTPBodyNotContains asserts that a specified handler returns a body that does not contain a string.

Returns whether the assertion was successful (true) or not (false).

Examples
	assertions.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
	success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!"
	failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, Bob!"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPBodyNotContains(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"net/url"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyNotContains(t *testing.T)
    	success := assert.HTTPBodyNotContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!")
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func httpBody(w http.ResponseWriter, r *http.Request) {
    	name := r.FormValue("name")
    	_, _ = fmt.Fprintf(w, "Hello, %s!", name)
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPBodyNotContains(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"net/url"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyNotContains(t *testing.T)
    	require.HTTPBodyNotContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!")
    	fmt.Println("passed")
    
    }
    
    func httpBody(w http.ResponseWriter, r *http.Request) {
    	name := r.FormValue("name")
    	_, _ = fmt.Fprintf(w, "Hello, %s!", name)
    }

HTTPError

HTTPError asserts that a specified handler returns an error status code.

Returns whether the assertion was successful (true) or not (false).

Examples
	assertions.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
	success: httpError, "GET", "/", nil
	failure: httpOK, "GET", "/", nil
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPError(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPError(t *testing.T)
    	success := assert.HTTPError(t, httpError, "GET", "/", nil)
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func httpError(w http.ResponseWriter, _ *http.Request) {
    	w.WriteHeader(http.StatusInternalServerError)
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPError(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPError(t *testing.T)
    	require.HTTPError(t, httpError, "GET", "/", nil)
    	fmt.Println("passed")
    
    }
    
    func httpError(w http.ResponseWriter, _ *http.Request) {
    	w.WriteHeader(http.StatusInternalServerError)
    }

HTTPRedirect

HTTPRedirect asserts that a specified handler returns a redirect status code.

Returns whether the assertion was successful (true) or not (false).

Examples
	assertions.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
	success: httpRedirect, "GET", "/", nil
	failure: httpError, "GET", "/", nil
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPRedirect(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPRedirect(t *testing.T)
    	success := assert.HTTPRedirect(t, httpRedirect, "GET", "/", nil)
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func httpRedirect(w http.ResponseWriter, _ *http.Request) {
    	w.WriteHeader(http.StatusTemporaryRedirect)
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPRedirect(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPRedirect(t *testing.T)
    	require.HTTPRedirect(t, httpRedirect, "GET", "/", nil)
    	fmt.Println("passed")
    
    }
    
    func httpRedirect(w http.ResponseWriter, _ *http.Request) {
    	w.WriteHeader(http.StatusTemporaryRedirect)
    }

HTTPStatusCode

HTTPStatusCode asserts that a specified handler returns a specified status code.

Returns whether the assertion was successful (true) or not (false).

Examples
	assertions.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
	success: httpOK, "GET", "/", nil, http.StatusOK
	failure: httpError, "GET", "/", nil, http.StatusOK
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPStatusCode(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPStatusCode(t *testing.T)
    	success := assert.HTTPStatusCode(t, httpOK, "GET", "/", nil, http.StatusOK)
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func httpOK(w http.ResponseWriter, _ *http.Request) {
    	w.WriteHeader(http.StatusOK)
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPStatusCode(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPStatusCode(t *testing.T)
    	require.HTTPStatusCode(t, httpOK, "GET", "/", nil, http.StatusOK)
    	fmt.Println("passed")
    
    }
    
    func httpOK(w http.ResponseWriter, _ *http.Request) {
    	w.WriteHeader(http.StatusOK)
    }

HTTPSuccess

HTTPSuccess asserts that a specified handler returns a success status code.

Returns whether the assertion was successful (true) or not (false).

Examples
	assertions.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
	success: httpOK, "GET", "/", nil
	failure: httpError, "GET", "/", nil
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPSuccess(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPSuccess(t *testing.T)
    	success := assert.HTTPSuccess(t, httpOK, "GET", "/", nil)
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func httpOK(w http.ResponseWriter, _ *http.Request) {
    	w.WriteHeader(http.StatusOK)
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestHTTPSuccess(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"net/http"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestHTTPSuccess(t *testing.T)
    	require.HTTPSuccess(t, httpOK, "GET", "/", nil)
    	fmt.Println("passed")
    
    }
    
    func httpOK(w http.ResponseWriter, _ *http.Request) {
    	w.WriteHeader(http.StatusOK)
    }

Other helpers

HTTPBody

HTTPBody is a helper that returns the HTTP body of the response. It returns the empty string if building a new request fails.


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Json

Asserting JSON Documents

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 5 functionalities. Generic assertions are marked with a .

JSONEq

JSONEq asserts that two JSON strings are semantically equivalent.

Expected and actual must be valid JSON.

Examples
	assertions.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
	success: `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`
	failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestJSONEq(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestJSONEq(t *testing.T)
    	success := assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestJSONEq(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestJSONEq(t *testing.T)
    	require.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
    	fmt.Println("passed")
    
    }

JSONEqBytes

JSONEqBytes asserts that two JSON slices of bytes are semantically equivalent.

Expected and actual must be valid JSON.

Examples
	assertions.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`))
	success: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)
	failure: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`[{"foo": "bar"}, {"hello": "world"}]`)
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestJSONEqBytes(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestJSONEqBytes(t *testing.T)
    	success := assert.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`))
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestJSONEqBytes(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestJSONEqBytes(t *testing.T)
    	require.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`))
    	fmt.Println("passed")
    
    }

JSONEqT[EDoc, ADoc Text]

JSONEqT asserts that two JSON documents are semantically equivalent.

The expected and actual arguments may be string or []byte. They do not need to be of the same type.

Expected and actual must be valid JSON.

Examples
	assertions.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`))
	success: `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)
	failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestJSONEqT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestJSONEqT(t *testing.T)
    	success := assert.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`))
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestJSONEqT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestJSONEqT(t *testing.T)
    	require.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`))
    	fmt.Println("passed")
    
    }

JSONMarshalAsT[EDoc Text]

JSONMarshalAsT wraps JSONEq after json.Marshal.

The input JSON may be a string or []byte.

It fails if the marshaling returns an error or if the expected JSON bytes differ semantically from the expected ones.

Examples
	actual := struct {
		A int `json:"a"`
	}{
		A: 10,
	}
	assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`)
	success: []byte(`{"A": "a"}`), dummyStruct{A: "a"}
	failure: `[{"foo": "bar"}, {"hello": "world"}]`, 1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestJSONMarshalAsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestJSONMarshalAsT(t *testing.T)
    	success := assert.JSONMarshalAsT(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"})
    	fmt.Printf("success: %t\n", success)
    
    }
    
    type dummyStruct struct {
    	A string
    	b int
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestJSONMarshalAsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestJSONMarshalAsT(t *testing.T)
    	require.JSONMarshalAsT(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"})
    	fmt.Println("passed")
    
    }
    
    type dummyStruct struct {
    	A string
    	b int
    }

JSONUnmarshalAsT[Object any, ADoc Text]

JSONUnmarshalAsT wraps Equal after json.Unmarshal.

The input JSON may be a string or []byte.

It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one.

Be careful not to wrap the expected object into an “any” interface if this is not what you expected: the unmarshaling would take this type to unmarshal as a mapstringany.

Examples
	expected := struct {
		A int `json:"a"`
	}{
		A: 10,
	}
	assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`)
	success: dummyStruct{A: "a"} , []byte(`{"A": "a"}`)
	failure: 1, `[{"foo": "bar"}, {"hello": "world"}]`
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestJSONUnmarshalAsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestJSONUnmarshalAsT(t *testing.T)
    	success := assert.JSONUnmarshalAsT(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    type dummyStruct struct {
    	A string
    	b int
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestJSONUnmarshalAsT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestJSONUnmarshalAsT(t *testing.T)
    	require.JSONUnmarshalAsT(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`))
    	fmt.Println("passed")
    
    }
    
    type dummyStruct struct {
    	A string
    	b int
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Number

Asserting Numbers

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 7 functionalities. Generic assertions are marked with a .

InDelta

InDelta asserts that the two numerals are within delta of each other.

Delta must be greater than or equal to zero.

Expected and actual values should convert to float64. To compare large integers that can’t be represented accurately as float64 (e.g. uint64), prefer InDeltaT to preserve the original type.

Behavior with IEEE floating point arithmetic

  • expected NaN is matched only by a NaN, e.g. this works: [InDeltaT](math.Sqrt, math.Sqrt, 0.0)
  • expected +Inf is matched only by a +Inf
  • expected -Inf is matched only by a -Inf
Examples
	assertions.InDelta(t, math.Pi, 22/7.0, 0.01)
	success: 1.0, 1.01, 0.02
	failure: 1.0, 1.1, 0.05
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInDelta(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInDelta(t *testing.T)
    	success := assert.InDelta(t, 1.0, 1.01, 0.02)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInDelta(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInDelta(t *testing.T)
    	require.InDelta(t, 1.0, 1.01, 0.02)
    	fmt.Println("passed")
    
    }

InDeltaMapValues

InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.

See InDelta.

Examples
	assertions.InDeltaMapValues(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.0}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.01}, 0.02)
	success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.0}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.01}, 0.02
	failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.0}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.1}, 0.05
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInDeltaMapValues(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInDeltaMapValues(t *testing.T)
    	success := assert.InDeltaMapValues(t, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInDeltaMapValues(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInDeltaMapValues(t *testing.T)
    	require.InDeltaMapValues(t, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02)
    	fmt.Println("passed")
    
    }

InDeltaSlice

InDeltaSlice is the same as InDelta, except it compares two slices.

See InDelta.

Examples
	assertions.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02)
	success: []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02
	failure: []float64{1.0, 2.0}, []float64{1.1, 2.1}, 0.05
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInDeltaSlice(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInDeltaSlice(t *testing.T)
    	success := assert.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInDeltaSlice(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInDeltaSlice(t *testing.T)
    	require.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02)
    	fmt.Println("passed")
    
    }

InDeltaT[Number Measurable]

InDeltaT asserts that the two numerals of the same type numerical type are within delta of each other.

InDeltaT accepts any go numeric type, including integer types.

The main difference with InDelta is that the delta is expressed with the same type as the values, not necessarily a float64.

Delta must be greater than or equal to zero.

Behavior with IEEE floating point arithmetic

  • expected NaN is matched only by a NaN, e.g. this works: InDeltaT(math.NaN, math.Sqrt, 0.0)
  • expected +Inf is matched only by a +Inf
  • expected -Inf is matched only by a -Inf
Examples
	assertions.InDeltaT(t, math.Pi, 22/7.0, 0.01)
	success: 1.0, 1.01, 0.02
	failure: 1.0, 1.1, 0.05
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInDeltaT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInDeltaT(t *testing.T)
    	success := assert.InDeltaT(t, 1.0, 1.01, 0.02)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInDeltaT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInDeltaT(t *testing.T)
    	require.InDeltaT(t, 1.0, 1.01, 0.02)
    	fmt.Println("passed")
    
    }

InEpsilon

InEpsilon asserts that expected and actual have a relative error less than epsilon.

Behavior with IEEE floating point arithmetic

  • expected NaN is matched only by a NaN, e.g. this works: [InDeltaT](math.NaN, math.Sqrt, 0.0)
  • expected +Inf is matched only by a +Inf
  • expected -Inf is matched only by a -Inf

Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer InDeltaT.

Formula:

  • If expected == 0: fail if |actual - expected| > epsilon
  • If expected != 0: fail if |actual - expected| > epsilon * |expected|

This allows InEpsilonT to work naturally across the full numeric range including zero.

Examples
	assertions.InEpsilon(t, 100.0, 101.0, 0.02)
	success: 100.0, 101.0, 0.02
	failure: 100.0, 110.0, 0.05
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInEpsilon(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInEpsilon(t *testing.T)
    	success := assert.InEpsilon(t, 100.0, 101.0, 0.02)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInEpsilon(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInEpsilon(t *testing.T)
    	require.InEpsilon(t, 100.0, 101.0, 0.02)
    	fmt.Println("passed")
    
    }

InEpsilonSlice

InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.

See InEpsilon.

Examples
	assertions.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02)
	success: []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02
	failure: []float64{100.0, 200.0}, []float64{110.0, 220.0}, 0.05
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInEpsilonSlice(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSlice(t *testing.T)
    	success := assert.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInEpsilonSlice(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSlice(t *testing.T)
    	require.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02)
    	fmt.Println("passed")
    
    }

InEpsilonT[Number Measurable]

InEpsilonT asserts that expected and actual have a relative error less than epsilon.

When expected is zero, epsilon is interpreted as an absolute error threshold, since relative error is mathematically undefined for zero values.

Unlike InDeltaT, which preserves the original type, InEpsilonT converts the expected and actual numbers to float64, since the relative error doesn’t make sense as an integer.

Behavior with IEEE floating point arithmetic

  • expected NaN is matched only by a NaN, e.g. this works: [InDeltaT](math.NaN, math.Sqrt, 0.0)
  • expected +Inf is matched only by a +Inf
  • expected -Inf is matched only by a -Inf

Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer InDeltaT.

Formula:

  • If expected == 0: fail if |actual - expected| > epsilon
  • If expected != 0: fail if |actual - expected| > epsilon * |expected|

This allows InEpsilonT to work naturally across the full numeric range including zero.

Examples
	assertions.InEpsilon(t, 100.0, 101.0, 0.02)
	success: 100.0, 101.0, 0.02
	failure: 100.0, 110.0, 0.05
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInEpsilonT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInEpsilonT(t *testing.T)
    	success := assert.InEpsilonT(t, 100.0, 101.0, 0.02)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestInEpsilonT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestInEpsilonT(t *testing.T)
    	require.InEpsilonT(t, 100.0, 101.0, 0.02)
    	fmt.Println("passed")
    
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Ordering

Asserting How Collections Are Ordered

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 10 functionalities. Generic assertions are marked with a .

IsDecreasing

IsDecreasing asserts that the collection is strictly decreasing.

Examples
	assertions.IsDecreasing(t, []int{2, 1, 0})
	assertions.IsDecreasing(t, []float{2, 1})
	assertions.IsDecreasing(t, []string{"b", "a"})
	success: []int{3, 2, 1}
	failure: []int{1, 2, 3}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsDecreasing(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsDecreasing(t *testing.T)
    	success := assert.IsDecreasing(t, []int{3, 2, 1})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsDecreasing(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsDecreasing(t *testing.T)
    	require.IsDecreasing(t, []int{3, 2, 1})
    	fmt.Println("passed")
    
    }

IsDecreasingT[OrderedSlice ~[]E, E Ordered]

IsDecreasingT asserts that a slice of Ordered is strictly decreasing.

Examples
	assertions.IsDecreasingT(t, []int{2, 1, 0})
	assertions.IsDecreasingT(t, []float{2, 1})
	assertions.IsDecreasingT(t, []string{"b", "a"})
	success: []int{3, 2, 1}
	failure: []int{1, 2, 3}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsDecreasingT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsDecreasingT(t *testing.T)
    	success := assert.IsDecreasingT(t, []int{3, 2, 1})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsDecreasingT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsDecreasingT(t *testing.T)
    	require.IsDecreasingT(t, []int{3, 2, 1})
    	fmt.Println("passed")
    
    }

IsIncreasing

IsIncreasing asserts that the collection is strictly increasing.

Examples
	assertions.IsIncreasing(t, []int{1, 2, 3})
	assertions.IsIncreasing(t, []float{1, 2})
	assertions.IsIncreasing(t, []string{"a", "b"})
	success: []int{1, 2, 3}
	failure: []int{1, 1, 2}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsIncreasing(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsIncreasing(t *testing.T)
    	success := assert.IsIncreasing(t, []int{1, 2, 3})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsIncreasing(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsIncreasing(t *testing.T)
    	require.IsIncreasing(t, []int{1, 2, 3})
    	fmt.Println("passed")
    
    }

IsIncreasingT[OrderedSlice ~[]E, E Ordered]

IsIncreasingT asserts that a slice of Ordered is strictly increasing.

Examples
	assertions.IsIncreasingT(t, []int{1, 2, 3})
	assertions.IsIncreasingT(t, []float{1, 2})
	assertions.IsIncreasingT(t, []string{"a", "b"})
	success: []int{1, 2, 3}
	failure: []int{1, 1, 2}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsIncreasingT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsIncreasingT(t *testing.T)
    	success := assert.IsIncreasingT(t, []int{1, 2, 3})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsIncreasingT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsIncreasingT(t *testing.T)
    	require.IsIncreasingT(t, []int{1, 2, 3})
    	fmt.Println("passed")
    
    }

IsNonDecreasing

IsNonDecreasing asserts that the collection is not strictly decreasing.

Examples
	assertions.IsNonDecreasing(t, []int{1, 1, 2})
	assertions.IsNonDecreasing(t, []float{1, 2})
	assertions.IsNonDecreasing(t, []string{"a", "b"})
	success: []int{1, 1, 2}
	failure: []int{2, 1, 0}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNonDecreasing(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasing(t *testing.T)
    	success := assert.IsNonDecreasing(t, []int{1, 1, 2})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNonDecreasing(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasing(t *testing.T)
    	require.IsNonDecreasing(t, []int{1, 1, 2})
    	fmt.Println("passed")
    
    }

IsNonDecreasingT[OrderedSlice ~[]E, E Ordered]

IsNonDecreasingT asserts that a slice of Ordered is not decreasing.

Examples
	assertions.IsNonDecreasingT(t, []int{1, 1, 2})
	assertions.IsNonDecreasingT(t, []float{1, 2})
	assertions.IsNonDecreasingT(t, []string{"a", "b"})
	success: []int{1, 1, 2}
	failure: []int{2, 1, 0}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNonDecreasingT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasingT(t *testing.T)
    	success := assert.IsNonDecreasingT(t, []int{1, 1, 2})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNonDecreasingT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasingT(t *testing.T)
    	require.IsNonDecreasingT(t, []int{1, 1, 2})
    	fmt.Println("passed")
    
    }

IsNonIncreasing

IsNonIncreasing asserts that the collection is not increasing.

Examples
	assertions.IsNonIncreasing(t, []int{2, 1, 1})
	assertions.IsNonIncreasing(t, []float{2, 1})
	assertions.IsNonIncreasing(t, []string{"b", "a"})
	success: []int{2, 1, 1}
	failure: []int{1, 2, 3}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNonIncreasing(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasing(t *testing.T)
    	success := assert.IsNonIncreasing(t, []int{2, 1, 1})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNonIncreasing(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasing(t *testing.T)
    	require.IsNonIncreasing(t, []int{2, 1, 1})
    	fmt.Println("passed")
    
    }

IsNonIncreasingT[OrderedSlice ~[]E, E Ordered]

IsNonIncreasingT asserts that a slice of Ordered is NOT strictly increasing.

Examples
	assertions.IsNonIncreasing(t, []int{2, 1, 1})
	assertions.IsNonIncreasing(t, []float{2, 1})
	assertions.IsNonIncreasing(t, []string{"b", "a"})
	success: []int{2, 1, 1}
	failure: []int{1, 2, 3}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNonIncreasingT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasingT(t *testing.T)
    	success := assert.IsNonIncreasingT(t, []int{2, 1, 1})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNonIncreasingT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasingT(t *testing.T)
    	require.IsNonIncreasingT(t, []int{2, 1, 1})
    	fmt.Println("passed")
    
    }

NotSortedT[OrderedSlice ~[]E, E Ordered]

NotSortedT asserts that the slice of Ordered is NOT sorted (i.e. non-strictly increasing).

Unlike IsDecreasingT, it accepts slices that are neither increasing nor decreasing.

Examples
	assertions.NotSortedT(t, []int{3, 2, 3})
	assertions.NotSortedT(t, []float{2, 1})
	assertions.NotSortedT(t, []string{"b", "a"})
	success: []int{3, 1, 3}
	failure: []int{1, 4, 8}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotSortedT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotSortedT(t *testing.T)
    	success := assert.NotSortedT(t, []int{3, 1, 3})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotSortedT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotSortedT(t *testing.T)
    	require.NotSortedT(t, []int{3, 1, 3})
    	fmt.Println("passed")
    
    }

SortedT[OrderedSlice ~[]E, E Ordered]

SortedT asserts that the slice of Ordered is sorted (i.e. non-strictly increasing).

Unlike IsIncreasingT, it accepts elements to be equal.

Examples
	assertions.SortedT(t, []int{1, 2, 3})
	assertions.SortedT(t, []float{1, 2})
	assertions.SortedT(t, []string{"a", "b"})
	success: []int{1, 1, 3}
	failure: []int{1, 4, 2}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSortedT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSortedT(t *testing.T)
    	success := assert.SortedT(t, []int{1, 1, 3})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestSortedT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestSortedT(t *testing.T)
    	require.SortedT(t, []int{1, 1, 3})
    	fmt.Println("passed")
    
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Panic

Asserting A Panic Behavior

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 4 functionalities.

NotPanics

NotPanics asserts that the code inside the specified function does NOT panic.

Examples
	assertions.NotPanics(t, func(){ RemainCalm() })
	success: func() { }
	failure: func() { panic("panicking") }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotPanics(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotPanics(t *testing.T)
    	success := assert.NotPanics(t, func() {
    	})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotPanics(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotPanics(t *testing.T)
    	require.NotPanics(t, func() {
    	})
    	fmt.Println("passed")
    
    }

Panics

Panics asserts that the code inside the specified function panics.

Examples
	assertions.Panics(t, func(){ GoCrazy() })
	success: func() { panic("panicking") }
	failure: func() { }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestPanics(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestPanics(t *testing.T)
    	success := assert.Panics(t, func() {
    		panic("panicking")
    	})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestPanics(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestPanics(t *testing.T)
    	require.Panics(t, func() {
    		panic("panicking")
    	})
    	fmt.Println("passed")
    
    }

PanicsWithError

PanicsWithError asserts that the code inside the specified function panics, and that the recovered panic value is an error that satisfies the EqualError comparison.

Examples
	assertions.PanicsWithError(t, "crazy error", func(){ GoCrazy() })
	success: ErrTest.Error(), func() { panic(ErrTest) }
	failure: ErrTest.Error(), func() { }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestPanicsWithError(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestPanicsWithError(t *testing.T)
    	success := assert.PanicsWithError(t, assert.ErrTest.Error(), func() {
    		panic(assert.ErrTest)
    	})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestPanicsWithError(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestPanicsWithError(t *testing.T)
    	require.PanicsWithError(t, assert.ErrTest.Error(), func() {
    		panic(assert.ErrTest)
    	})
    	fmt.Println("passed")
    
    }

PanicsWithValue

PanicsWithValue asserts that the code inside the specified function panics, and that the recovered panic value equals the expected panic value.

Examples
	assertions.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
	success: "panicking", func() { panic("panicking") }
	failure: "panicking", func() { }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestPanicsWithValue(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestPanicsWithValue(t *testing.T)
    	success := assert.PanicsWithValue(t, "panicking", func() {
    		panic("panicking")
    	})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestPanicsWithValue(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestPanicsWithValue(t *testing.T)
    	require.PanicsWithValue(t, "panicking", func() {
    		panic("panicking")
    	})
    	fmt.Println("passed")
    
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Safety

Checks Against Leaked Resources (Goroutines, File Descriptors)

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 2 functionalities.

NoFileDescriptorLeak

NoFileDescriptorLeak ensures that no file descriptor leaks from inside the tested function.

This assertion works on Linux only (via /proc/self/fd). On other platforms, the test is skipped.

NOTE: this assertion is not compatible with parallel tests. File descriptors are a process-wide resource; concurrent tests opening files would cause false positives.

Sockets, pipes, and anonymous inodes are filtered out by default, as these are typically managed by the Go runtime.

Concurrency

NoFileDescriptorLeak is not compatible with parallel tests. File descriptors are a process-wide resource; any concurrent I/O from other goroutines may cause false positives.

Calls to NoFileDescriptorLeak are serialized with a mutex to prevent multiple leak checks from interfering with each other.

Examples
	NoFileDescriptorLeak(t, func() {
		// code that should not leak file descriptors
	})
	success: func() {}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoFileDescriptorLeak(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"runtime"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	if runtime.GOOS != "linux" {
    		// This example is only runnable on linux. On other platforms, the assertion skips the test.
    		// We force the expected output below, so that tests don't fail on other platforms.
    		fmt.Println("success: true")
    
    		return
    	}
    
    	t := new(testing.T) // should come from testing, e.g. func TestNoFileDescriptorLeak(t *testing.T)
    	success := assert.NoFileDescriptorLeak(t, func() {
    	})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoFileDescriptorLeak(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"runtime"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	if runtime.GOOS != "linux" {
    		// This example is only runnable on linux. On other platforms, the assertion skips the test.
    		// We force the expected output below, so that tests don't fail on other platforms.
    		fmt.Println("passed")
    
    		return
    	}
    
    	t := new(testing.T) // should come from testing, e.g. func TestNoFileDescriptorLeak(t *testing.T)
    	require.NoFileDescriptorLeak(t, func() {
    	})
    	fmt.Println("passed")
    
    }

NoGoRoutineLeak

NoGoRoutineLeak ensures that no goroutine did leak from inside the tested function.

NOTE: only the go routines spawned from inside the tested function are checked for leaks. No filter or configuration is needed to exclude “known go routines”.

Resource cleanup should be done inside the tested function, and not using testing.T.Cleanup, as t.Cleanup is called after the leak check.

Edge cases

  • if the tested function panics leaving behind leaked goroutines, these are detected.
  • if the tested function calls runtime.Goexit (e.g. from testing.T.FailNow) leaving behind leaked goroutines, these are detected.
  • if a panic occurs in one of the leaked go routines, it cannot be recovered with certainty and the calling program will usually panic.

Concurrency

NoGoRoutineLeak may be used safely in parallel tests.

Examples
	NoGoRoutineLeak(t, func() {
		...
	},
	"should not leak any go routine",
	)
	success: func() {}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNoGoRoutineLeak(t *testing.T)
    	success := assert.NoGoRoutineLeak(t, func() {
    	})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	blocker := make(chan struct{})
    	var wg sync.WaitGroup
    
    	defer func() {
    		// clean resources _after_ the test
    		close(blocker)
    		wg.Wait()
    	}()
    
    	wg.Add(1)
    	// This examplifies how a function that leaks a goroutine is detected.
    	result := assert.NoGoRoutineLeak(t, func() { // true when there is no leak
    		go func() {
    			defer wg.Done()
    			<-blocker // leaked: blocks until cleanup
    		}()
    	})
    
    	// Error message from test would typically return the leaked goroutine, e.g.:
    	// #	0x69c8e8	github.com/go-openapi/testify/v2/assert_test.ExampleNoGoRoutineLeak.func2.1+0x48	.../assert_adhoc_example_7_test.go:30
    	fmt.Printf("has leak: %t", !result)
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNoGoRoutineLeak(t *testing.T)
    	require.NoGoRoutineLeak(t, func() {
    	})
    	fmt.Println("passed")
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)
    // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers
    // SPDX-License-Identifier: Apache-2.0
    
    package main
    
    import (
    	"fmt"
    	"sync"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(mockFailNowT) // normally provided by test
    	// Since this test is failing and calls [runtime.Goexit], we need a mock to
    	// avoid the example trigger a panick.
    
    	blocker := make(chan struct{})
    	var wg sync.WaitGroup
    
    	defer func() {
    		// clean resources _after_ the test
    		close(blocker)
    		wg.Wait()
    	}()
    
    	wg.Add(1)
    	// This examplifies how a function that leaks a goroutine is detected.
    	require.NoGoRoutineLeak(t, func() { // true when there is no leak
    		go func() {
    			defer wg.Done()
    			<-blocker // leaked: blocks until cleanup
    		}()
    	})
    
    	// Error message from test would typically return the leaked goroutine, e.g.:
    	// #	0x69c8e8	github.com/go-openapi/testify/v2/assert_test.ExampleNoGoRoutineLeak.func2.1+0x48	.../assert_adhoc_example_7_test.go:30
    	fmt.Printf("passed: %t", !t.Failed())
    
    }
    
    type mockFailNowT struct {
    	failed bool
    }
    
    // Helper is like [testing.T.Helper] but does nothing.
    func (mockFailNowT) Helper() {}
    
    func (m *mockFailNowT) Errorf(format string, args ...any) {
    	_ = format
    	_ = args
    }
    
    func (m *mockFailNowT) FailNow() {
    	m.failed = true
    }
    
    func (m *mockFailNowT) Failed() bool {
    	return m.failed
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

String

Asserting Strings

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 4 functionalities. Generic assertions are marked with a .

NotRegexp

NotRegexp asserts that a specified regular expression does not match a string.

See Regexp.

Examples
	assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
	assertions.NotRegexp(t, "^start", "it's not starting")
	success: "^start", "not starting"
	failure: "^start", "starting"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotRegexp(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotRegexp(t *testing.T)
    	success := assert.NotRegexp(t, "^start", "not starting")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotRegexp(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotRegexp(t *testing.T)
    	require.NotRegexp(t, "^start", "not starting")
    	fmt.Println("passed")
    
    }

NotRegexpT[Rex RegExp, ADoc Text]

NotRegexpT asserts that a specified regular expression does not match a string.

See RegexpT.

Examples
	assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
	assertions.NotRegexp(t, "^start", "it's not starting")
	success: "^start", "not starting"
	failure: "^start", "starting"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotRegexpT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotRegexpT(t *testing.T)
    	success := assert.NotRegexpT(t, "^start", "not starting")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotRegexpT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotRegexpT(t *testing.T)
    	require.NotRegexpT(t, "^start", "not starting")
    	fmt.Println("passed")
    
    }

Regexp

Regexp asserts that a specified regular expression matches a string.

The regular expression may be passed as a regexp.Regexp, a string or a []byte and will be compiled.

The actual argument to be matched may be a string, []byte or anything that prints as a string with fmt.Sprint.

Examples
	assertions.Regexp(t, regexp.MustCompile("start"), "it's starting")
	assertions.Regexp(t, "start...$", "it's not starting")
	success: "^start", "starting"
	failure: "^start", "not starting"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestRegexp(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestRegexp(t *testing.T)
    	success := assert.Regexp(t, "^start", "starting")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestRegexp(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestRegexp(t *testing.T)
    	require.Regexp(t, "^start", "starting")
    	fmt.Println("passed")
    
    }

RegexpT[Rex RegExp, ADoc Text]

RegexpT asserts that a specified regular expression matches a string.

The actual argument to be matched may be a string or []byte.

See Regexp.

Examples
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestRegexpT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestRegexpT(t *testing.T)
    	success := assert.RegexpT(t, "^start", "starting")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestRegexpT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestRegexpT(t *testing.T)
    	require.RegexpT(t, "^start", "starting")
    	fmt.Println("passed")
    
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Testing

Mimics Methods From The Testing Standard Library

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 2 functionalities.

Fail

Fail reports a failure through.

Examples
	assertions.Fail(t, "failed")
	failure: "failed"

FailNow

FailNow fails test.

Examples
	assertions.FailNow(t, "failed")
	failure: "failed"


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Time

Asserting Times And Durations

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 2 functionalities.

WithinDuration

WithinDuration asserts that the two times are within duration delta of each other.

Examples
	assertions.WithinDuration(t, time.Now(), 10*time.Second)
	success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second
	failure: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 10, 0, time.UTC), 1*time.Second
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestWithinDuration(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestWithinDuration(t *testing.T)
    	success := assert.WithinDuration(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestWithinDuration(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestWithinDuration(t *testing.T)
    	require.WithinDuration(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second)
    	fmt.Println("passed")
    
    }

WithinRange

WithinRange asserts that a time is within a time range (inclusive).

Examples
	assertions.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
	success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)
	failure: time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestWithinRange(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestWithinRange(t *testing.T)
    	success := assert.WithinRange(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC))
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestWithinRange(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestWithinRange(t *testing.T)
    	require.WithinRange(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC))
    	fmt.Println("passed")
    
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Type

Asserting Types Rather Than Values

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 10 functionalities. Generic assertions are marked with a .

Implements

Implements asserts that an object is implemented by the specified interface.

Examples
	assertions.Implements(t, (*MyInterface)(nil), new(MyObject))
	success: ptr(dummyInterface), new(testing.T)
	failure: (*error)(nil), new(testing.T)
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestImplements(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestImplements(t *testing.T)
    	success := assert.Implements(t, ptr(dummyInterface), new(testing.T))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing
    var (
    	staticVar = "static string"
    
    	dummyInterface assert.T
    )
    
    func ptr[T any](value T) *T {
    	p := value
    
    	return &p
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestImplements(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestImplements(t *testing.T)
    	require.Implements(t, ptr(dummyInterface), new(testing.T))
    	fmt.Println("passed")
    
    }
    
    //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing
    var (
    	staticVar = "static string"
    
    	dummyInterface require.T
    )
    
    func ptr[T any](value T) *T {
    	p := value
    
    	return &p
    }

IsNotOfTypeT[EType any]

IsNotOfTypeT asserts that an object is not of a given type.

Examples
	assertions.IsOfType[MyType](t,myVar)
	success: 123.123
	failure: myType(123.123)
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNotOfTypeT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNotOfTypeT(t *testing.T)
    	success := assert.IsNotOfTypeT[myType](t, 123.123)
    	fmt.Printf("success: %t\n", success)
    
    }
    
    type myType float64
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNotOfTypeT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNotOfTypeT(t *testing.T)
    	require.IsNotOfTypeT[myType](t, 123.123)
    	fmt.Println("passed")
    
    }
    
    type myType float64

IsNotType

IsNotType asserts that the specified objects are not of the same type.

Examples
	assertions.IsNotType(t, &NotMyStruct{}, &MyStruct{})
	success: int32(123), int64(456)
	failure: 123, 456
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNotType(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNotType(t *testing.T)
    	success := assert.IsNotType(t, int32(123), int64(456))
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsNotType(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsNotType(t *testing.T)
    	require.IsNotType(t, int32(123), int64(456))
    	fmt.Println("passed")
    
    }

IsOfTypeT[EType any]

IsOfTypeT asserts that an object is of a given type.

Examples
	assertions.IsOfTypeT[MyType](t,myVar)
	success: myType(123.123)
	failure: 123.123
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsOfTypeT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsOfTypeT(t *testing.T)
    	success := assert.IsOfTypeT[myType](t, myType(123.123))
    	fmt.Printf("success: %t\n", success)
    
    }
    
    type myType float64
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsOfTypeT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsOfTypeT(t *testing.T)
    	require.IsOfTypeT[myType](t, myType(123.123))
    	fmt.Println("passed")
    
    }
    
    type myType float64

IsType

IsType asserts that the specified objects are of the same type.

Examples
	assertions.IsType(t, &MyStruct{}, &MyStruct{})
	success: 123, 456
	failure: int32(123), int64(456)
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsType(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsType(t *testing.T)
    	success := assert.IsType(t, 123, 456)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestIsType(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestIsType(t *testing.T)
    	require.IsType(t, 123, 456)
    	fmt.Println("passed")
    
    }

Kind

Kind asserts that the reflect.Kind of a given object matches the expected reflect.Kind.

Kind reflects the concrete value stored in the object. The nil value (or interface with nil value) are comparable to reflect.Invalid. See also reflect.Value.Kind.

Examples
	assertions.Kind(t, reflect.String, "Hello World")
	success: reflect.String, "hello"
	failure: reflect.String, 0
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestKind(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"reflect"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestKind(t *testing.T)
    	success := assert.Kind(t, reflect.String, "hello")
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestKind(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"reflect"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestKind(t *testing.T)
    	require.Kind(t, reflect.String, "hello")
    	fmt.Println("passed")
    
    }

NotImplements

NotImplements asserts that an object does not implement the specified interface.

Examples
	assertions.NotImplements(t, (*MyInterface)(nil), new(MyObject))
	success: (*error)(nil), new(testing.T)
	failure: ptr(dummyInterface), new(testing.T)
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotImplements(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotImplements(t *testing.T)
    	success := assert.NotImplements(t, (*error)(nil), new(testing.T))
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotImplements(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotImplements(t *testing.T)
    	require.NotImplements(t, (*error)(nil), new(testing.T))
    	fmt.Println("passed")
    
    }

NotKind

NotKind asserts that the reflect.Kind of a given object does not match the expected reflect.Kind.

Kind reflects the concrete value stored in the object. The nil value (or interface with nil value) are comparable to reflect.Invalid. See also reflect.Value.Kind.

Examples
	assertions.NotKind(t, reflect.Int, "Hello World")
	success: reflect.String, 0
	failure: reflect.String, "hello"
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotKind(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"reflect"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotKind(t *testing.T)
    	success := assert.NotKind(t, reflect.String, 0)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotKind(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"reflect"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotKind(t *testing.T)
    	require.NotKind(t, reflect.String, 0)
    	fmt.Println("passed")
    
    }

NotZero

NotZero asserts that i is not the zero value for its type.

Examples
	assertions.NotZero(t, obj)
	success: 1
	failure: 0
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotZero(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotZero(t *testing.T)
    	success := assert.NotZero(t, 1)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotZero(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotZero(t *testing.T)
    	require.NotZero(t, 1)
    	fmt.Println("passed")
    
    }

Zero

Zero asserts that i is the zero value for its type.

Examples
	assertions.Zero(t, obj)
	success: 0
	failure: 1
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestZero(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestZero(t *testing.T)
    	success := assert.Zero(t, 0)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestZero(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestZero(t *testing.T)
    	require.Zero(t, 0)
    	fmt.Println("passed")
    
    }


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Yaml

Asserting Yaml Documents

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 5 functionalities. Generic assertions are marked with a .

YAMLEq

YAMLEq asserts that two YAML strings are equivalent.

See YAMLEqBytes.

Examples
	panic: "key: value", "key: value"
	should panic without the yaml feature enabled.

YAMLEqBytes

YAMLEqBytes asserts that two YAML slices of bytes are equivalent.

Expected and actual must be valid YAML.

Important

By default, this function is disabled and will panic.

To enable it, you should add a blank import like so:

import(
  "github.com/go-openapi/testify/enable/yaml/v2"
)
Examples
	expected := `---
	key: value
	---
	key: this is a second document, it is not evaluated
	`
	actual := `---
	key: value
	---
	key: this is a subsequent document, it is not evaluated
	`
	assertions.YAMLEq(t, expected, actual)
	panic: []byte("key: value"), []byte("key: value")
	should panic without the yaml feature enabled.

YAMLEqT[EDoc, ADoc Text]

YAMLEqT asserts that two YAML documents are equivalent.

The expected and actual arguments may be string or []byte. They do not need to be of the same type.

See YAMLEqBytes.

Examples
	panic: "key: value", "key: value"
	should panic without the yaml feature enabled.

YAMLMarshalAsT[EDoc Text]

YAMLMarshalAsT wraps YAMLEq after yaml.Marshal.

The input YAML may be a string or []byte.

It fails if the marshaling returns an error or if the expected YAML bytes differ semantically from the expected ones.

Examples
	actual := struct {
		A int `yaml:"a"`
	}{
		A: 10,
	}
	assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`)
	panic: "key: value", "key: value"
	should panic without the yaml feature enabled.

YAMLUnmarshalAsT[Object any, ADoc Text]

YAMLUnmarshalAsT wraps Equal after yaml.Unmarshal.

The input YAML may be a string or []byte.

It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one.

Be careful not to wrap the expected object into an “any” interface if this is not what you expected: the unmarshaling would take this type to unmarshal as a mapstringany.

Examples
	expected := struct {
		A int `yaml:"a"`
	}{
		A: 10,
	}
	assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`)
	panic: "key: value", "key: value"
	should panic without the yaml feature enabled.


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Common

Other Uncategorized Helpers

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 3 functionalities.


Other helpers

CallerInfo

CallerInfo returns an array of strings containing the file and line number of each stack frame leading from the current test to the assert call that failed.

SignatureUsage
assertions.CallerInfo() []stringinternal implementation

Source: github.com/go-openapi/testify/v2/internal/assertions#CallerInfo

Maintainer Note

it is not necessary to export CallerInfo. This should remain an internal implementation detail.

ObjectsAreEqual

ObjectsAreEqual determines if two objects are considered equal.

This function does no assertion of any kind.

ObjectsAreEqualValues

ObjectsAreEqualValues gets whether two objects are equal, or if their values are equal.


Generated with github.com/go-openapi/testify/codegen/v2

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Usage

Guides, examples and tutorials about testify/v2.

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Subsections of Usage

Usage

TL;DR

Learn testify’s naming conventions (assert vs require, format variants, generic T suffix), argument order patterns, and how to navigate 76+ assertions organized into 18 domains. Start here to understand the API structure.

Testify v2 provides over 40 core assertion types (76+ functions including inverse variants and all naming styles) organized into clear domains. This guide explains how to navigate the API and use the naming conventions effectively.

How the API is Organized

Assertions are grouped by domain for easier discovery:

DomainExamplesCount
BooleanTrue, False2
EqualityEqual, NotEqual, EqualValues, Same, Exactly8
ComparisonGreater, Less, Positive8
CollectionContains, Len, Empty, ElementsMatch18
ErrorError, NoError, ErrorIs, ErrorAs, ErrorContains8
TypeIsType, Implements, Zero7
StringRegexp, NotRegexp4
NumericInDelta, InEpsilon6
OrderingIsIncreasing, Sorted8
PanicPanics, NotPanics4
OthersHTTP, JSON, YAML, Time, File assertions12
Browse by Domain

See the complete API Reference organized by domain for a detailed documentation of all assertions.

Quick Reference

  • Examples - Practical code examples for common testing scenarios
  • API Reference - Complete assertion catalog organized by domain
  • Generics Guide - Using type-safe assertions with the T suffix
  • Changes - All changes since fork from stretchr/testify
  • pkg.go.dev - godoc API reference with full signatures

Finding the Right Assertion

  1. Browse the API Reference by domain (e.g., “Collection” for slice operations)
  2. Search in the API Reference (use search box)
  3. Check (or search) the provided Examples for practical usage patterns
  4. Check pkg.go.dev for alphabetical listing
  5. Use your editor’s Go to Definition on any assertion
  6. Use your IDE’s autocomplete - type assert. and explore

API Conventions

Understanding the naming patterns helps you find and use the right assertions quickly.

Package Choice: assert vs require

  • assert - Non-Fatal

    Use when: Tests should continue after failures to gather more context

    import (
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func TestUser(t *testing.T) {
    	user := getUser()
    
    	assert.NotNil(t, user)              // ✓ Returns false
    	assert.Equal(t, "Alice", user.Name) // Still runs
    	assert.True(t, user.Active)         // Still runs
    }

    Returns: bool indicating success/failure

  • require - Fatal

    Use when: Test cannot continue meaningfully after failure

    import (
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func TestUser(t *testing.T) {
    	user := getUser()
    
    	require.NotNil(t, user)              // ✓ Calls t.FailNow() if fails
    	require.Equal(t, "Alice", user.Name) // Safe to proceed
    	require.True(t, user.Active)         // user is guaranteed non-nil
    }

    Returns: Nothing (void) - stops test on failure

Function Variants

Each assertion comes in multiple variants following consistent patterns:

PatternExampleDescription
BaseEqual(t, expected, actual)Standard assertion
Format (f suffix)Equalf(t, expected, actual, "checking %s", name)With custom message
Generic (T suffix)EqualT(t, expected, actual)Type-safe variant
Generic + Format (Tf suffix)EqualTf(t, expected, actual, "checking %s", name)Type-safe with message

The f suffix follows Go’s standard library convention (like Printf, Errorf): it accepts a format string followed by arguments for custom failure messages.

When to Use Each Variant
  • Base/Generic: Use by default - testify provides detailed failure messages
  • Format variants: Add context when testing similar values in loops or complex scenarios
  • Generic (T suffix): Prefer for compile-time type safety and better performance

Inverse Assertions

Most assertions come with their opposite variant, typically formed by adding a Not prefix:

AssertionInversePattern
EqualNotEqualNot prefix
NilNotNilNot prefix
EmptyNotEmptyNot prefix
ContainsNotContainsNot prefix
ZeroNotZeroNot prefix
SameNotSameNot prefix
PanicsNotPanicsNot prefix
RegexpNotRegexpNot prefix

Exceptions: Some assertions use semantic opposites instead of the Not prefix:

AssertionInverseReason
TrueFalseSemantic opposites (NotTrue doesn’t sound natural)
PositiveNegativeSemantic opposites, except for 0 which is neither
GreaterLessOrEqualComparative opposites (and not NotGreater)
GreaterOrEqualLessComparative opposites
Why Semantic Opposites?

These exceptions follow natural English usage:

  • Testing for False is clearer than testing for “not true”
  • (strictly) Negative numbers are semantically opposite to (strictly) Positive, unless when Zero, and not “not positive”
  • Less is the natural opposite of Greater in comparisons

More semantic opposites:

AssertionInverseReason
EventuallyNeverSemantic opposites for polling conditions

Not inverses: Some assertions are independent checks, not inverses of each other:

AssertionsRelationship
IsIncreasing / IsDecreasingIndependent checks (a sequence can be neither)
IsNonIncreasing / IsNonDecreasingIndependent checks (a sequence can be neither)
Sorted / NotSortedTrue inverse pair using Not prefix

Generic variants: All inverse assertions have corresponding generic variants (suffix T or Tf):

  • NotEqualT, FalseT, NegativeT, IsDecreasingT, etc.

Argument Order Patterns

Most assertions follow the “expected, actual” pattern, but several categories use different conventions:

Standard Pattern: Expected, Actual

The majority of assertions check an actual value against an expected value:

assert.Equal(t, expected, actual)
assert.NotEqual(t, expected, actual)
assert.InDelta(t, expected, actual, delta)
assert.JSONEq(t, expected, actual)
assert.YAMLEq(t, expected, actual)
assert.WithinDuration(t, expected, actual, delta)
assert.Implements(t, (*interface)(nil), object)  // Expected interface, actual object

Comparison Operators: e1, e2

Comparison assertions express the relationship directly (reads as “assert e1 > e2”):

assert.Greater(t, e1, e2)           // Asserts: e1 > e2
assert.GreaterOrEqual(t, e1, e2)    // Asserts: e1 >= e2
assert.Less(t, e1, e2)              // Asserts: e1 < e2
assert.LessOrEqual(t, e1, e2)       // Asserts: e1 <= e2

Exceptions using Different Argument Orders

Unary checks (test a single value):

assert.True(t, value)
assert.False(t, value)
assert.Nil(t, value)
assert.Empty(t, object)
assert.Zero(t, value)
assert.Positive(t, value)
assert.Negative(t, value)
assert.Error(t, err)
assert.NoError(t, err)
assert.Panics(t, panicFunc)

Object-first pattern (object under test, then expected property):

assert.Len(t, object, expectedLength)  // Object first, expected length second
assert.IsType(t, expectedType, object) // Expected type first, object second

Container-first pattern (container, then element/subset):

assert.Contains(t, container, element)     // Container first, element second
assert.StringContains(t, str, substring)   // String first, substring second
assert.SliceContains(t, slice, element)    // Slice first, element second
assert.Subset(t, list, subset)             // Superset first, subset second
assert.ElementsMatch(t, listA, listB)      // Either order works (symmetric)

Error assertions (error first, then expected property):

assert.ErrorContains(t, err, substring)    // Error first, expected substring second
assert.ErrorIs(t, err, target)             // Error first, target error second
assert.ErrorAs(t, err, &target)            // Error first, target pointer second
assert.EqualError(t, err, expectedString)  // Error first, expected message second

Special cases:

assert.HTTPSuccess(t, handler, method, url, values) // Handler first, HTTP params follow
assert.Eventually(t, condition, waitFor, tick)      // Condition first, timing params follow
Finding Argument Order

When unsure about argument order:

  • Check the API Reference for detailed signatures
  • Use IDE autocomplete to see parameter names
  • Consult pkg.go.dev for complete documentation

Forward Methods

Create an Assertions object to reduce repetition in tests with many assertions.

Both styles are equivalent - choose based on your preference and test structure.

⚠️ Generic assertions are not directly available as forward methods (this is a limitation of go generics).

However, the forward-style assertion may use generic assertions like shown below.

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUser(t *testing.T) {
	user := getUser()

	assert.NotNil(t, user)
	assert.Equal(t, "Alice", user.Name)
	assert.True(t, user.Active)
	assert.Greater(t, user.Age, 0)
}
import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUser(t *testing.T) {
	a := assert.New(t) // Create once
	user := getUser()

	a.NotNil(user) // No 't' needed
	a.Equal("Alice", user.Name)
	a.True(user.Active)
}

This pattern overcomes the go limitation so you may use a “forward-style” and still benefit from generics.

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUser(t *testing.T) {
	a := assert.New(t) // Create once
	user := getUser()

	a.NotNil(user) // No 't' needed
	assert.EqualT(a.T, "Alice", user.Name) // use a.T as an escape hatch to call generics
	a.True(user.Active)
}

Common Usage Patterns

Pattern 1: Table-Driven Tests

import (
	"slices"
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestCalculation(t *testing.T) {
	tests := slices.Values([]struct {
		name     string
		input    int
		expected int
	}{
		{"positive", 5, 25},
		{"negative", -3, 9},
		{"zero", 0, 0},
	})

	for tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := square(tt.input)
			assert.Equal(t, tt.expected, result)
		})
	}
}

Pattern 2: Multiple Assertions (assert for context)

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUserValidation(t *testing.T) {
	user := createUser()

	// Use assert to see all failures
	assert.NotEmpty(t, user.Name)  // Check name
	assert.NotEmpty(t, user.Email) // Check email
	assert.Greater(t, user.Age, 0) // Check age
	// All assertions run - see complete picture
}

Pattern 3: Early Exit (use require for prerequisites)

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

func TestDatabaseQuery(t *testing.T) {
	db := connectDB()
	require.NotNil(t, db) // Stop if no connection

	result := db.Query("SELECT * FROM users")
	require.NoError(t, result.Error) // Stop if query fails

	// Safe to proceed - db and result are valid
	assert.NotEmpty(t, result.Rows)
}

Pattern 4: Type-Safe Generics

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestTypeSafety(t *testing.T) {
	expected := []int{1, 2, 3}
	actual := getNumbers()

	// Compiler checks types at compile time
	assert.ElementsMatchT(t, expected, actual)
	assert.GreaterT(t, len(actual), 0)

	// If getNumbers() changes return type,
	// compiler catches it immediately
}

Getting Started

  1. Import the package:

    import "github.com/go-openapi/testify/v2/assert"
    // or
    import "github.com/go-openapi/testify/v2/require"
  2. Choose your style:

    • Package-level: assert.Equal(t, expected, actual)
    • Forward methods: a := assert.New(t); a.Equal(expected, actual)
  3. Explore by domain:

  4. Use generics for type safety:

    • See Generics Guide for type-safe assertions
    • Add T suffix for compile-time type checking

Best Practices

Do:

  • Use require for prerequisites that make subsequent assertions meaningless (or will possibly panic)
  • Use assert when you want to see all failures in a test
  • Prefer generic variants (*T functions) for compile-time type safety
  • Use format variants (*f) to add context in complex scenarios
  • Browse by domain in the API reference to discover relevant assertions

Don’t:

  • Don’t mix assert and require randomly - be intentional
  • Don’t add unnecessary format messages - testify provides detailed output
  • Don’t ignore compiler errors from generic variants - they reveal design issues
  • Don’t forget that both packages provide the same assertions with different behavior

Ready to Test

Next Steps:


Customization

Using a Custom YAML Unmarshaler

By default, testify uses gopkg.in/yaml.v3 for YAML assertions (e.g. YAMLEq) when you import the standard enable/yaml/v2 package.

However, you can register a custom YAML unmarshaler to use alternative libraries like goccy/go-yaml, either because you need additional features such as colored error messages or better performance.

How It Works

The YAML support in testify works through a registration mechanism:

  1. internal/assertions/yaml.go calls yaml.Unmarshal() - an abstraction layer
  2. The abstraction layer panics if no unmarshaler is registered
  3. The enable/yaml/v2 package registers gopkg.in/yaml.v3 via init() when imported (e.g. on blank import)
  4. You can register a custom unmarshaler using enable/stubs/yaml.EnableYAMLWithUnmarshal()

Example: Using goccy/go-yaml

Create a custom enable package in your test code:

package testutil

import (
	goccyyaml "github.com/goccy/go-yaml"
	yamlstub "github.com/go-openapi/testify/v2/enable/stubs/yaml"
)

func init() {
	// Register goccy/go-yaml as the YAML unmarshaler
	yamlstub.EnableYAMLWithUnmarshal(goccyyaml.Unmarshal)
}

Then import your custom enable package in your tests:

// File: mypackage/user_test.go
package mypackage

import (
	"testing"

	_ "yourmodule/internal/testutil" // Register goccy/go-yaml

	"github.com/go-openapi/testify/v2/assert"
)

func TestUserYAML(t *testing.T) {
	expected := `
name: Alice
email: alice@example.com
age: 30
`
	actual := serializeUser(getUser())

	// Now uses goccy/go-yaml under the hood
	assert.YAMLEq(t, expected, actual)
}

Why Use a Custom YAML Library?

Different YAML libraries offer different trade-offs:

gopkg.in/yaml.v3 (default):

  • De facto standard library for Go YAML
  • Widely used and well-tested
  • Complete YAML 1.2 support

github.com/goccy/go-yaml:

  • Better performance (up to 2-3x faster)
  • Colored error messages for debugging
  • Better error reporting with line/column numbers
  • JSON-like syntax support
  • Comment preservation (useful for config testing)

Important Notes

  1. Register once: Call EnableYAMLWithUnmarshal() only once, typically in an init() function
  2. Not concurrent-safe: The registration is global and should happen during package or main program initialization
  3. Signature compatibility: The custom unmarshaler must match the signature func([]byte, any) error
  4. No mixing: Don’t import both github.com/go-openapi/testify/enable/yaml/v2 and your custom enable package - choose one

Advanced: Wrapping Unmarshalers

You can also wrap an unmarshaler to add custom behavior:

package testutil

import (
	"fmt"
	"log"

	goccyyaml "github.com/goccy/go-yaml"

	yamlstub "github.com/go-openapi/testify/v2/enable/stubs/yaml"
)

func init() {
	// Wrap the unmarshaler to add logging or validation
	yamlstub.EnableYAMLWithUnmarshal(func(data []byte, v any) error {
		// Custom pre-processing
		if len(data) == 0 {
			return fmt.Errorf("empty YAML document")
		}

		// Call the actual unmarshaler
		err := goccyyaml.Unmarshal(data, v)
		// Custom post-processing
		if err != nil {
			log.Printf("YAML unmarshal error: %v", err)
		}

		return err
	})
}

This pattern allows you to add logging, validation, or transformation logic around any YAML library.


See Also

  • Examples - Practical code examples for common testing scenarios
  • Tutorial - Comprehensive guide to writing great tests with testify patterns
  • Generics Guide - Type-safe assertions with compile-time checking
  • Migration Guide - Migrating from stretchr/testify v1
  • API Reference - Complete assertion catalog organized by domain
Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Examples

TL;DR

If you’ve already used github.com/stretchr/testify, adopting v2 will be straightforward.

More examples to showcase generic assertions specifically may be found here.

Quick Start

The simplest way to get started with testify is using the assert package:

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestCalculator(t *testing.T) {
	result := Add(2, 3)
	assert.Equal(t, 5, result)
}

assert vs require

Use assert when you want tests to continue after a failure:

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUser(t *testing.T) {
	user := GetUser(123)

	// All three checks run, even if the first fails
	assert.NotNil(t, user)
	assert.Equal(t, "Alice", user.Name)
	assert.Equal(t, 25, user.Age)
}

Use require when subsequent checks depend on earlier ones:

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

func TestUser(t *testing.T) {
	user := GetUser(123)

	// Stop immediately if user is nil (prevents panic on next line)
	require.NotNil(t, user)

	// Only runs if user is not nil
	assert.Equal(t, "Alice", user.Name)
}

Rule of thumb: Use require for preconditions, assert for actual checks.


Common Assertions

Equality

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestEquality(t *testing.T) {
	// Basic equality
	assert.Equal(t, 42, actualValue)

	// Deep equality for slices, maps, structs
	assert.Equal(t, []int{1, 2, 3}, result)

	// Check inequality
	assert.NotEqual(t, 0, result)

	// Type-converting equality (123 == int64(123))
	assert.EqualValues(t, 123, int64(123))
}

Collections

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestCollections(t *testing.T) {
	list := []string{"apple", "banana", "cherry"}

	// Check if collection contains an element
	assert.Contains(t, list, "banana")
	assert.NotContains(t, list, "orange")

	// Check length
	assert.Len(t, list, 3)

	// Check if empty
	assert.NotEmpty(t, list)
	assert.Empty(t, []string{})

	// Check if all elements match (order doesn't matter)
	assert.ElementsMatch(t, []int{3, 1, 2}, []int{1, 2, 3})
}

Errors

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestErrors(t *testing.T) {
	// Check if function returns an error
	err := DoSomething()
	assert.Error(t, err)

	// Check if function succeeds
	err = DoSomethingElse()
	assert.NoError(t, err)

	// Check specific error message
	err = Divide(10, 0)
	assert.EqualError(t, err, "division by zero")

	// Check if error contains text
	assert.ErrorContains(t, err, "division")

	// Check error type with errors.Is
	assert.ErrorIs(t, err, ErrDivisionByZero)
}

Nil Checks

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestNil(t *testing.T) {
	var ptr *User

	assert.Nil(t, ptr)

	user := &User{Name: "Alice"}
	assert.NotNil(t, user)
}

Boolean and Comparisons

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestBooleans(t *testing.T) {
	assert.True(t, isValid)
	assert.False(t, hasErrors)

	// Numeric comparisons
	assert.Greater(t, 10, 5)
	assert.GreaterOrEqual(t, 10, 10)
	assert.Less(t, 5, 10)
	assert.LessOrEqual(t, 5, 5)
}

Assertion Variants

Testify provides multiple ways to call assertions:

1. Package-Level Functions

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

func TestPackageLevel(t *testing.T) {
	assert.Equal(t, 42, result)
	require.NotNil(t, user)
}

2. Formatted Variants (Custom Messages)

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

func TestFormatted(t *testing.T) {
	// Add custom failure message with formatting
	assert.Equalf(t, 42, result, "expected answer to be %d", 42)
	require.NotNilf(t, user, "user %d should exist", userID)
}

3. Forward Methods (Cleaner Syntax)

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

func TestForward(t *testing.T) {
	a := assert.New(t)
	r := require.New(t)

	// No need to pass 't' each time
	a.Equal(42, result)
	a.NotEmpty(list)

	r.NotNil(user)
	r.NoError(err)
}

4. Forward Methods with Formatting

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestForwardFormatted(t *testing.T) {
	a := assert.New(t)

	a.Equalf(42, result, "expected answer to be %d", 42)
	a.Lenf(list, 3, "expected %d items", 3)
}

Recommendation: Use package-level functions for simple tests, forward methods for tests with many assertions.


Table-Driven Tests

The idiomatic Go pattern for testing multiple cases should be:

import (
	"slices"
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestAdd(t *testing.T) {
	tests := slices.Values([]struct {
		name     string
		a, b     int
		expected int
	}{
		{"positive numbers", 2, 3, 5},
		{"negative numbers", -2, -3, -5},
		{"mixed signs", -2, 3, 1},
		{"with zero", 0, 5, 5},
	})

	for tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := Add(tt.a, tt.b)
			assert.Equal(t, tt.expected, result)
		})
	}
}

With forward methods for cleaner syntax:

func TestAdd(t *testing.T) {
    tests := slices.Values([]struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive numbers", 2, 3, 5},
        {"negative numbers", -2, -3, -5},
    })

    for tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            a := assert.New(t)

            result := Add(tt.a, tt.b)
            a.Equal(tt.expected, result)
            a.Greater(result, tt.a)
        })
    }
}

Real-World Examples

Testing HTTP Handlers

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

func TestUserHandler(t *testing.T) {
	req := httptest.NewRequest("GET", "/users/123", nil)
	w := httptest.NewRecorder()

	handler := NewUserHandler()
	handler.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)

	// Check response body contains expected data
	body := w.Body.String()
	assert.Contains(t, body, `"name":"Alice"`)
	assert.Contains(t, body, `"id":123`)
}

Testing JSON

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestJSONResponse(t *testing.T) {
	expected := `{"name":"Alice","age":25}`
	actual := `{"age":25,"name":"Alice"}`

	// JSONEq compares JSON semantically (ignores key order, whitespace)
	assert.JSONEq(t, expected, actual)
}

Testing with Subtests

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUserOperations(t *testing.T) {
	user := &User{ID: 123, Name: "Alice"}

	t.Run("creation", func(t *testing.T) {
		assert.NotNil(t, user)
		assert.Equal(t, 123, user.ID)
	})

	t.Run("update", func(t *testing.T) {
		user.Name = "Bob"
		assert.Equal(t, "Bob", user.Name)
	})

	t.Run("deletion", func(t *testing.T) {
		err := DeleteUser(user.ID)
		assert.NoError(t, err)
	})
}

Testing Panics

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestPanics(t *testing.T) {
	// Function should panic
	assert.Panics(t, func() {
		Divide(10, 0)
	})

	// Function should NOT panic
	assert.NotPanics(t, func() {
		Divide(10, 2)
	})

	// Function should panic with specific value
	assert.PanicsWithValue(t, "division by zero", func() {
		Divide(10, 0)
	})
}

Advanced Patterns

Setup and Teardown

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

func TestWithSetup(t *testing.T) {
	// Setup
	db := setupTestDatabase(t)
	t.Cleanup(func() {
		db.Close() // Teardown
	})

	// Test
	user := &User{Name: "Alice"}
	err := db.Save(user)
	require.NoError(t, err)

	// Verify
	loaded, err := db.Find(user.ID)
	require.NoError(t, err)
	assert.Equal(t, "Alice", loaded.Name)
}

Helper Functions

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUsers(t *testing.T) {
	user := GetUser(123)
	assertUserValid(t, user) // Failures point to this line, not inside helper
}

func assertUserValid(t *testing.T, user *User) {
	t.Helper() // Mark as helper for better error messages

	assert.NotNil(t, user)
	assert.NotEmpty(t, user.Name)
	assert.Greater(t, user.Age, 0)
}

Combining Multiple Assertions

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUserCompleteness(t *testing.T) {
	a := assert.New(t)
	user := GetUser(123)

	// Chain multiple checks cleanly
	a.NotNil(user)
	a.NotEmpty(user.Name)
	a.NotEmpty(user.Email)
	a.Greater(user.Age, 0)
	a.True(user.Active)
}

Asynchronous Testing

Testify provides three assertions for testing asynchronous code: Eventually, Never, and EventuallyWith.

Warning

Asynchronous testing may sometimes be unavoidable. It should be avoided whenever possible.

Async tests (with timeouts, ticks etc) may easily become flaky under heavy concurrence on small CI runners.

When you’ve control over the code you test, always prefer sync tests, possibly with well-designed mocks.

Eventually: Wait for a Condition to Become True

Use Eventually when testing code that updates state asynchronously (background goroutines, event loops, caches).

import (
	"sync"
	"testing"
	"time"

	"github.com/go-openapi/testify/v2/assert"
)

func TestBackgroundProcessor(t *testing.T) {
	// Simulate a background processor that updates state
	var processed bool
	var mu sync.Mutex

	go func() {
		time.Sleep(50 * time.Millisecond)
		mu.Lock()
		processed = true
		mu.Unlock()
	}()

	// Wait up to 200ms for the background task to complete,
	// checking every 10ms
	assert.Eventually(t, func() bool {
		mu.Lock()
		defer mu.Unlock()
		return processed
	}, 200*time.Millisecond, 10*time.Millisecond,
		"background processor should have completed")
}

// Real-world example: Testing cache warming
func TestCacheWarming(t *testing.T) {
	cache := NewCache()
	cache.StartWarmup() // Populates cache in background

	// Verify cache becomes ready within 5 seconds
	assert.Eventually(t, func() bool {
		return cache.IsReady() && cache.Size() > 0
	}, 5*time.Second, 100*time.Millisecond,
		"cache should warm up and contain entries")
}

Never: Ensure a Condition Remains False

Use Never to verify that something undesirable never happens during a time window (no data corruption, no invalid state).

import (
	"sync/atomic"
	"testing"
	"time"

	"github.com/go-openapi/testify/v2/assert"
)

func TestNoDataCorruption(t *testing.T) {
	var counter atomic.Int32
	stopChan := make(chan struct{})
	defer close(stopChan)

	// Start multiple goroutines incrementing safely
	for i := 0; i < 10; i++ {
		go func() {
			ticker := time.NewTicker(5 * time.Millisecond)
			defer ticker.Stop()
			for {
				select {
				case <-stopChan:
					return
				case <-ticker.C:
					counter.Add(1)
				}
			}
		}()
	}

	// Verify counter never goes negative (indicating corruption)
	assert.Never(t, func() bool {
		return counter.Load() < 0
	}, 500*time.Millisecond, 20*time.Millisecond,
		"counter should never go negative")
}

// Real-world example: Testing rate limiter doesn't exceed threshold
func TestRateLimiter(t *testing.T) {
	limiter := NewRateLimiter(100) // 100 requests per second max
	stopChan := make(chan struct{})
	defer close(stopChan)

	// Hammer the limiter with requests
	for i := 0; i < 50; i++ {
		go func() {
			ticker := time.NewTicker(1 * time.Millisecond)
			defer ticker.Stop()
			for {
				select {
				case <-stopChan:
					return
				case <-ticker.C:
					limiter.Allow()
				}
			}
		}()
	}

	// Verify we never exceed the rate limit over 2 seconds
	assert.Never(t, func() bool {
		return limiter.CurrentRate() > 120 // 20% tolerance
	}, 2*time.Second, 50*time.Millisecond,
		"rate limiter should never exceed threshold")
}

EventuallyWith: Complex Async Assertions

Use EventuallyWith when you need multiple assertions to pass together. The CollectT parameter lets you make regular assertions.

import (
	"testing"
	"time"

	"github.com/go-openapi/testify/v2/assert"
)

func TestAPIEventualConsistency(t *testing.T) {
	// Simulate an eventually-consistent API
	api := NewEventuallyConsistentAPI()
	api.CreateUser("alice", "alice@example.com")

	// Wait for the user to be fully replicated across all shards
	// All conditions must pass in the same tick
	assert.EventuallyWith(t, func(c *assert.CollectT) {
		user, err := api.GetUser("alice")

		// All these assertions must pass together
		assert.NoError(c, err, "user should be retrievable")
		assert.NotNil(c, user, "user should exist")
		assert.EqualT(c, "alice@example.com", user.Email, "email should match")
		assert.True(c, user.Replicated, "user should be replicated")
		assert.GreaterOrEqual(c, user.ReplicaCount, 3, "should be on at least 3 replicas")
	}, 10*time.Second, 500*time.Millisecond,
		"user should be eventually consistent across all replicas")
}

// Real-world example: Testing distributed cache sync
func TestDistributedCacheSync(t *testing.T) {
	primary := NewCacheNode("primary")
	replica1 := NewCacheNode("replica1")
	replica2 := NewCacheNode("replica2")

	// Connect nodes for replication
	primary.AddReplica(replica1)
	primary.AddReplica(replica2)

	// Write to primary
	primary.Set("key", "value", 5*time.Minute)

	// Verify value propagates to all replicas with correct TTL
	assert.EventuallyWith(t, func(c *assert.CollectT) {
		val1, ttl1, ok1 := replica1.Get("key")
		val2, ttl2, ok2 := replica2.Get("key")

		// All replicas must have the value
		assert.True(c, ok1, "replica1 should have the key")
		assert.True(c, ok2, "replica2 should have the key")

		// Values must match
		assert.EqualT(c, "value", val1, "replica1 value should match")
		assert.EqualT(c, "value", val2, "replica2 value should match")

		// TTL should be approximately the same (within 1 second)
		assert.InDelta(c, 5*time.Minute, ttl1, float64(time.Second),
			"replica1 TTL should be close to original")
		assert.InDelta(c, 5*time.Minute, ttl2, float64(time.Second),
			"replica2 TTL should be close to original")
	}, 5*time.Second, 100*time.Millisecond,
		"cache value should replicate to all nodes with correct TTL")
}

// Advanced: Using require in EventuallyWith to fail fast
func TestEventuallyWithRequire(t *testing.T) {
	api := NewAPI()

	assert.EventuallyWith(t, func(c *assert.CollectT) {
		resp, err := api.HealthCheck()

		// Use require to stop checking this tick if request fails
		// This prevents nil pointer panics on subsequent assertions
		assert.NoError(c, err, "health check should not error")
		if err != nil {
			return // Skip remaining checks this tick
		}

		// Now safe to check response fields
		assert.EqualT(c, "healthy", resp.Status)
		assert.Greater(c, resp.Uptime, 0)
		assert.NotEmpty(c, resp.Version)
	}, 30*time.Second, 1*time.Second,
		"API should become healthy")
}

Key differences:

  • Eventually: Simple boolean condition, use for single checks
  • Never: Opposite of Eventually, verifies condition stays false
  • EventuallyWith: Complex checks with multiple assertions, use when you need detailed failure messages

Best practices:

  1. Choose appropriate timeouts: long enough for async operations, short enough for fast test feedback
  2. Choose appropriate tick intervals: frequent enough to catch state changes, infrequent enough to avoid overhead
  3. Use EventuallyWith when you need to understand which assertion failed
  4. Use Eventually for simple boolean conditions (faster, simpler)
  5. Use Never to verify invariants over time (no race conditions, no invalid state)

Goroutine Leak Detection

Use NoGoRoutineLeak to verify that your code doesn’t leak goroutines. This is critical for long-running applications, connection pools, and worker patterns.

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestWorkerPool(t *testing.T) {
	assert.NoGoRoutineLeak(t, func() {
		pool := NewWorkerPool(10)
		pool.Start()

		// Submit work
		pool.Submit(func() { /* do something */ })

		// Cleanup MUST happen inside the tested function
		pool.Shutdown()
	})
}

Why Use It?

Traditional goroutine leak detection (like go.uber.org/goleak) requires maintaining filter lists to exclude known system goroutines. This approach is brittle and prone to false positives when:

  • Running parallel tests
  • Using connection pools (database, HTTP, gRPC)
  • Background runtime goroutines change between Go versions

NoGoRoutineLeak uses pprof labels instead of stack-trace heuristics:

  • Only goroutines spawned by your tested function are checked
  • Pre-existing goroutines (runtime, connection pools, other tests) are ignored automatically
  • No configuration or filter lists needed
  • Works safely with t.Parallel()

Real-World Example: Testing a Server

import (
	"net/http"
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestHTTPServer(t *testing.T) {
	assert.NoGoRoutineLeak(t, func() {
		// Start server
		server := &http.Server{Addr: ":0", Handler: myHandler}
		go server.ListenAndServe()

		// Do some requests...
		resp, _ := http.Get("http://" + server.Addr + "/health")
		resp.Body.Close()

		// Shutdown MUST happen inside the tested function
		server.Shutdown(context.Background())
	})
}

Important: Cleanup Inside the Tested Function

Resource cleanup must happen inside the tested function, not via t.Cleanup():

// ❌ WRONG: t.Cleanup runs AFTER the leak check
func TestWrong(t *testing.T) {
	var server *Server
	t.Cleanup(func() { server.Stop() }) // Too late!

	assert.NoGoRoutineLeak(t, func() {
		server = StartServer()
		// Leak detected because server is still running
	})
}

// ✅ CORRECT: cleanup inside tested function
func TestCorrect(t *testing.T) {
	assert.NoGoRoutineLeak(t, func() {
		server := StartServer()
		defer server.Stop() // Cleanup happens before leak check
		// ... test code ...
	})
}

Edge Cases

  • Panics: If the tested function panics while goroutines are still running, the leak is detected and reported along with the panic
  • t.FailNow()/runtime.Goexit(): Leaks are still detected even if the tested function exits early
  • Transitive goroutines: Goroutines spawned by child goroutines inherit the label and are tracked

Extensible assertions

The Assertions type may be extended to fit your needs like so.

   import (
   	"fmt"
   	"strings"

	"github.com/go-openapi/testify/v2/assert"
   )

   // Assertions is a customized version of [assert.Assertions].
   type Assertions struct {
       *assert.Assertions
   }

   func New(t assert.T) *Assertions {
       return &Assertions{
           Assertions: assert.New(t),
       }
   }
   
   // StartsWith asserts that the string starts with the given prefix.
   //
   // Examples:
   //
   //	success: "hello world", "hello"
   //	failure: "hello world", "bye"
   func (a *Assertions) StartsWith(str, prefix string, msgAndArgs ...any) bool {
        if h, ok := a.T.(assert.H); ok {
   		    h.Helper() // preserve the original failing location
   	    }

   	    if !strings.HasPrefix(str, prefix) {
   		    return a.Fail(fmt.Sprintf("Expected %q to start with %q", str, prefix), msgAndArgs...)
   	    }

   	    return true
    }

YAML Support (Optional)

YAML assertions require explicit opt-in:

import (
	"testing"

	_ "github.com/go-openapi/testify/enable/yaml/v2" // Enable YAML support
	"github.com/go-openapi/testify/v2/assert"
)

func TestYAML(t *testing.T) {
	expected := `
name: Alice
age: 25
`
	actual := `
age: 25
name: Alice
`

	// YAMLEq compares YAML semantically
	assert.YAMLEq(t, expected, actual)
}

Note: Without the enable/yaml import, YAML assertions will panic with a helpful message.


Colorized Output (Optional)

Testify can colorize test failure output for better readability. This is an opt-in feature.

Enabling Colors

import (
	"testing"

	_ "github.com/go-openapi/testify/enable/colors/v2" // Enable colorized output
	"github.com/go-openapi/testify/v2/assert"
)

func TestExample(t *testing.T) {
	assert.Equal(t, "expected", "actual") // Failure will be colorized
}

Activation

Colors are activated via command line flag or environment variable:

# Via flag
go test -v -testify.colorized ./...

# Via environment variable
TESTIFY_COLORIZED=true go test -v ./...

Themes

Two themes are available for different terminal backgrounds:

# Dark theme (default) - bright colors for dark terminals
go test -v -testify.colorized ./...

# Light theme - normal colors for light terminals
go test -v -testify.colorized -testify.theme=light ./...

# Or via environment
TESTIFY_COLORIZED=true TESTIFY_THEME=light go test -v ./...

CI Environments

By default, colorization is disabled when output is not a terminal. To force colors in CI environments that support ANSI codes:

TESTIFY_COLORIZED=true TESTIFY_COLORIZED_NOTTY=true go test -v ./...

What Gets Colorized

  • Expected values in assertion failures (green)
  • Actual values in assertion failures (red)
  • Diff output:
    • Deleted lines (red)
    • Inserted lines (yellow)
    • Context lines (green)

Note: Without the enable/colors import, output remains uncolored (no panic, just no colors).

See screenshot.


Best Practices

  1. Use require for preconditions - Stop test immediately if setup fails
  2. Use assert for actual checks - See all failures in one test run
  3. Add custom messages for complex checks - Use formatted variants when assertion failure needs context
  4. Prefer table-driven tests - Test multiple cases systematically
  5. Use forward methods for many assertions - Reduces repetition in long tests
  6. Keep tests focused - One logical concept per test function
  7. Use subtests for related scenarios - Group related checks with t.Run()
  8. Mark helpers with t.Helper() - Get better error locations

Migration from stdlib testing

Before (stdlib):

import "testing"

func TestOld(t *testing.T) {
	result := Calculate(5)
	if result != 10 {
		t.Errorf("Expected 10, got %d", result)
	}
	if len(items) == 0 {
		t.Error("Expected non-empty items")
	}
}

After (testify):

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestNew(t *testing.T) {
	a := assert.New(t)

	result := Calculate(5)
	a.Equal(10, result)
	a.NotEmpty(items)
}

Benefits:

  • More readable - assertions read like English
  • Better error messages - shows expected vs actual automatically
  • Less boilerplate - no manual formatting
  • More assertions - Contains, ElementsMatch, JSONEq, etc.

See Also

  • Tutorial - Comprehensive guide to writing great tests with testify patterns
  • Usage Guide - API conventions, naming patterns, and how to navigate the documentation
  • Generics Guide - Type-safe assertions for better compile-time checking
  • Migration Guide - Migrating from stretchr/testify v1 to this fork
  • API Reference - Complete assertion catalog organized by domain
Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Tutorial

TL;DR

Master the iterator pattern for table-driven tests, understand when to use require vs assert, and learn testify best practices. Essential patterns and anti-patterns for writing maintainable, effective Go tests.

What makes a good test?

A good test is:

  1. Focused - Tests one logical concept
  2. Independent - Can run in any order, in parallel
  3. Repeatable - Same input always produces same result
  4. Fast - Runs quickly to encourage frequent execution
  5. Have clear expectations - Failure messages immediately show what broke

With testify, you write tests that read like documentation:

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

func TestUserCreation(t *testing.T) {
	user := CreateUser("alice@example.com")

	require.NotNil(t, user)
	assert.Equal(t, "alice@example.com", user.Email) // if user is nil, will fail and stop before
	assert.True(t, user.Active)
}
tip

Adopt a test layout similar to your functionality.

# ❌ Don't do this - confusing
boolean.go
file.go
all_test.go
// ✅ Better - clear mapping between features and tests
boolean.go
boolean_test.go
file.go
file_test.go

The assertions are self-documenting - you can read the test and immediately understand what behavior is being verified.


Patterns

Simple test logic

Oftentimes, much of the test logic can be replaced by a proper use of require.

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

// ❌ Don't do this - repetitive and hard to maintain
func TestUserCreation(t *testing.T) {
	user := CreateUser("alice@example.com")

	if assert.NotNil(t, user) {
		assert.Equal(t, "alice@example.com", user.Email) // if user is nil, will skip this test
		assert.True(t, user.Active)
	}
}
import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

// ✅ Better - linear flow, no indented subcases
func TestUserCreation(t *testing.T) {
	user := CreateUser("alice@example.com")

	require.NotNil(t, user)
	assert.Equal(t, "alice@example.com", user.Email) // if user is nil, will fail and stop before
	assert.True(t, user.Active)
}

Table-Driven Tests with Iterator Pattern

The iterator pattern is the idiomatic way to write table-driven tests in Go 1.23+. This repository uses it extensively, and you should too.

Why Table-Driven Tests?

Instead of writing separate test functions for each case:

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

// ❌ Don't do this - repetitive and hard to maintain
func TestAdd_PositiveNumbers(t *testing.T) {
	result := Add(2, 3)
	assert.Equal(t, 5, result)
}

func TestAdd_NegativeNumbers(t *testing.T) {
	result := Add(-2, -3)
	assert.Equal(t, -5, result)
}

func TestAdd_MixedSigns(t *testing.T) {
	result := Add(-2, 3)
	assert.Equal(t, 1, result)
}

Write one test function with multiple cases:

// ✅ Better - all cases in one place
func TestAdd(t *testing.T) {
    // All test cases defined once
    // Test logic written once
    // Easy to add new cases
    for c := range addTestCases() {
        t.Run(c.name, func(t *testing.T) {
            t.Parallel()

            result := Add(c.a, c.b)
            assert.Equal(t, c.expected, result)
        })
    }
}

func addTestCases() iter.Seq[addTestCase] {
    ...
}

The Iterator Pattern

Structure:

import (
	"iter"
	"slices"
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

// 1. Define a test case struct
type addTestCase struct {
	name     string
	a, b     int
	expected int
}

// 2. Create an iterator function returning iter.Seq[T]
func addTestCases() iter.Seq[addTestCase] {
	return slices.Values([]addTestCase{
		{
			name:     "positive numbers",
			a:        2,
			b:        3,
			expected: 5,
		},
		{
			name:     "negative numbers",
			a:        -2,
			b:        -3,
			expected: -5,
		},
		{
			name:     "mixed signs",
			a:        -2,
			b:        3,
			expected: 1,
		},
		{
			name:     "with zero",
			a:        0,
			b:        5,
			expected: 5,
		},
	})
}

// 3. Test function iterates over cases using range
func TestAdd(t *testing.T) {
	t.Parallel()

	for c := range addTestCases() {
		t.Run(c.name, func(t *testing.T) {
			t.Parallel()

			result := Add(c.a, c.b)
			assert.Equal(t, c.expected, result)
		})
	}
}

Why This Pattern Is Better

Clean separation of concerns:

  • Test data (in iterator function) separate from test logic (in test function)
  • Easy to see all test cases at a glance
  • Easy to add new cases without touching test logic

Type safety:

  • Compiler enforces struct fields
  • No risk of wrong number of arguments
  • IDE autocomplete works perfectly

Excellent for parallel execution:

  • Both the outer test and subtests can run in parallel
  • t.Parallel() catches race conditions early

Reusable:

  • Iterator functions can be reused across multiple test functions
  • Share test cases between related tests

Maintainable:

  • Adding a case: just append to the slice
  • Changing test logic: edit one place
  • Renaming fields: IDE refactoring works

Comparison with Traditional Pattern

Traditional inline pattern:

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestAdd(t *testing.T) {
	tests := []struct {
		name     string
		a, b     int
		expected int
	}{
		{"positive", 2, 3, 5},
		{"negative", -2, -3, -5},
		// Test data mixed with test function
		// Hard to reuse
		// No named fields - order matters
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := Add(tt.a, tt.b)
			assert.Equal(t, tt.expected, result)
		})
	}
}

Iterator pattern:

import (
	"iter"
	"slices"
	"testing"
)

// Test logic separate and clean
func TestAdd(t *testing.T) {
	t.Parallel()
	for c := range addTestCases() { // Clean iteration
		// ...
	}
}

type addTestCase struct {
	name     string
	a, b     int
	expected int
}

// Test data in separate function - clean, reusable
func addTestCases() iter.Seq[addTestCase] {
	return slices.Values([]addTestCase{
		{
			name:     "positive numbers", // Named fields
			a:        2,                  // Self-documenting
			b:        3,
			expected: 5,
		},
		// More cases...
	})
}

When to Use Iterator Pattern

Always use it for:

  • Any test with 2+ test cases
  • Tests requiring complex setup per case
  • Tests that benefit from parallel execution
  • Any table-driven test scenario

Example - complex setup:

import (
	"iter"
	"slices"
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUserValidation(t *testing.T) {
	t.Parallel()

	for c := range userValidationCases() {
		t.Run(c.name, func(t *testing.T) {
			t.Parallel()

			err := ValidateUser(c.user)

			if c.shouldErr {
				assert.Error(t, err)
				assert.ErrorContains(t, err, c.errMsg)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

type userValidationCase struct {
	name      string
	user      User
	shouldErr bool
	errMsg    string
}

func userValidationCases() iter.Seq[userValidationCase] {
	return slices.Values([]userValidationCase{
		{
			name: "valid user",
			user: User{
				Name:  "Alice",
				Email: "alice@example.com",
				Age:   25,
			},
			shouldErr: false,
		},
		{
			name: "missing email",
			user: User{
				Name: "Bob",
				Age:  30,
			},
			shouldErr: true,
			errMsg:    "email is required",
		},
		{
			name: "invalid age",
			user: User{
				Name:  "Charlie",
				Email: "charlie@example.com",
				Age:   -5,
			},
			shouldErr: true,
			errMsg:    "age must be positive",
		},
	})
}

Using testify with Iterator Pattern

The iterator pattern works beautifully with testify’s forward methods:

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUserOperations(t *testing.T) {
	t.Parallel()

	for c := range userOperationCases() {
		t.Run(c.name, func(t *testing.T) {
			t.Parallel()
			a := assert.New(t) // Forward assertion object

			user := PerformOperation(c.input)

			// Clean assertions without repeating 't'
			a.NotNil(user)
			a.Equal(c.expectedName, user.Name)
			a.Greater(user.ID, 0)
		})
	}
}

Helper Functions with t.Helper()

When extracting common assertions into helper functions, use t.Helper() to get better error messages:

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func assertUserValid(t *testing.T, user *User) {
	t.Helper() // Makes test failures point to the caller

	assert.NotNil(t, user)
	assert.NotEmpty(t, user.Name)
	assert.NotEmpty(t, user.Email)
	assert.Greater(t, user.Age, 0)
}

func TestUserCreation(t *testing.T) {
	user := CreateUser("alice@example.com")

	// If this fails, error points HERE, not inside assertUserValid
	assertUserValid(t, user)
}

Without t.Helper(), failures would show the line number inside assertUserValid, making it harder to find the actual failing test.


Parallel Test Execution

Always use t.Parallel() unless you have a specific reason not to:

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestAdd(t *testing.T) {
	t.Parallel() // Outer test runs in parallel

	for c := range addTestCases() {
		t.Run(c.name, func(t *testing.T) {
			t.Parallel() // Each subtest runs in parallel

			result := Add(c.a, c.b)
			assert.Equal(t, c.expected, result)
		})
	}
}

Benefits:

  • Tests run faster
  • Catches race conditions and shared state bugs
  • Encourages writing independent tests

When NOT to use parallel:

  • Tests that modify global state
  • Tests that use the same external resource (file, database, etc.)
  • Integration tests with shared setup

Setup and Teardown

Use defer for cleanup:

func TestDatabaseOperations(t *testing.T) {
    db := setupTestDatabase(t)
    t.Cleanup(func() {
        _ = db.Close()  // Always runs, even if test fails
    }

    user := &User{Name: "Alice"}
    err := db.Save(user)
    require.NoError(t, err)  // Stop if save fails

    loaded, err := db.Find(user.ID)
    require.NoError(t, err)
    assert.Equal(t, "Alice", loaded.Name)
}

Pattern for resources:

  1. Create resource
  2. Immediately defer cleanup
  3. Use the resource
  4. Cleanup happens automatically

Edge Cases to Test

Always include these test categories:

1. Empty/Zero Values

{
    name:     "empty string",
    input:    "",
    expected: defaultValue,
},
{
    name:     "nil slice",
    input:    nil,
    expected: emptyResult,
},

2. Single Element

{
    name:     "single item",
    input:    []string{"only"},
    expected: "only",
},

3. Multiple Elements

{
    name:     "multiple items",
    input:    []string{"first", "second", "third"},
    expected: "first,second,third",
},

4. Boundary Conditions

{
    name:     "maximum value",
    input:    math.MaxInt64,
    expected: overflow,
},
{
    name:     "special characters",
    input:    "hello@#$%world",
    expected: sanitized,
},

Testing Errors

Bad practice - checking error string:

// ❌ Fragile - breaks if error message changes
if err == nil || err.Error() != "division by zero" {
    t.Error("wrong error")
}

Good practice - checking error chain:

// ✅ Semantic error checking
assert.Error(t, err)
assert.ErrorContains(t, err, "division")

// ✅ Check error type (possibly wrapped)
assert.ErrorIs(t, err, ErrDivisionByZero)

// ✅ Check for specific error message
assert.EqualError(t, err, "division by zero")

Complete Example

Here’s a complete example showing all patterns together:

package calculator_test

import (
	"iter"
	"slices"
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

type divideTestCase struct {
	name      string
	a, b      float64
	expected  float64
	shouldErr bool
}

func divideTestCases() iter.Seq[divideTestCase] {
	return slices.Values([]divideTestCase{
		{
			name:      "positive numbers",
			a:         10,
			b:         2,
			expected:  5,
			shouldErr: false,
		},
		{
			name:      "negative dividend",
			a:         -10,
			b:         2,
			expected:  -5,
			shouldErr: false,
		},
		{
			name:      "division by zero",
			a:         10,
			b:         0,
			shouldErr: true,
		},
		{
			name:      "zero dividend",
			a:         0,
			b:         5,
			expected:  0,
			shouldErr: false,
		},
	})
}

func TestDivide(t *testing.T) {
	t.Parallel()

	for c := range divideTestCases() {
		t.Run(c.name, func(t *testing.T) {
			t.Parallel()

			result, err := Divide(c.a, c.b)

			if c.shouldErr {
				assert.Error(t, err)
				assert.ErrorIs(t, err, ErrDivisionByZero)
			} else {
				require.NoError(t, err)
				assert.Equal(t, c.expected, result)
			}
		})
	}
}

Best Practices Summary

  1. Use the iterator pattern - iter.Seq[T] for all table-driven tests
  2. Separate test data from test logic - Iterator functions are your test data
  3. Use t.Parallel() - Both outer tests and subtests
  4. Use t.Helper() - In assertion helper functions
  5. Use require for preconditions - Stop test if setup fails
  6. Use assert for checks - See all failures
  7. Test edge cases - Empty, single, multiple, boundary conditions
  8. Use forward methods - assert.New(t) for tests with many assertions
  9. Use semantic error checking - ErrorIs, ErrorContains, not string comparison
  10. Keep tests focused - One logical concept per test

Examples in This Repository

See real-world usage of these patterns:

  • Iterator Pattern: codegen/internal/generator/funcmaps/funcmaps_test.go
  • Domain Tests: codegen/internal/generator/domains/domains_test.go
  • Assertion Tests: internal/assertions/*_test.go
  • Comprehensive Coverage: codegen/internal/scanner/comments-parser/ (all test files)

Study these to see the patterns in action!


See Also

  • Examples - Practical code examples covering common testing scenarios
  • Usage Guide - API conventions and how to navigate the documentation
  • Generics Guide - Type-safe assertions with compile-time checking
  • Migration Guide - Migrating from stretchr/testify v1
  • API Reference - Complete assertion catalog organized by domain
Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Generics

Testify v2 provides 42 generic assertion functions that offer compile-time type safety alongside the traditional reflection-based assertions. Generic variants are identified by the T suffix (e.g., EqualT, GreaterT, ElementsMatchT).

Type Safety First

Generic assertions catch type mismatches when writing tests, not when running them. The performance improvements (1.2x-81x faster) are a bonus on top of this primary benefit.

Quick Start

Generic assertions work exactly like their reflection-based counterparts, but with compile-time type checking:

  • Reflection-based
    import (
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func TestUser(t *testing.T) {
    	expected := 42
    	actual := getUserAge()
    
    	// Compiles, but type errors appear at runtime
    	assert.Equal(t, expected, actual)
    }
  • Generic (Type-safe)
    import (
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func TestUser(t *testing.T) {
    	expected := 42
    	actual := getUserAge()
    
    	// Compiler checks types immediately
    	assert.EqualT(t, expected, actual)
    }

When to Use Generic Variants

✅ Use Generic Variants (*T functions) When:

  1. Testing with known concrete types - The most common case

    assert.EqualT(t, 42, result)              // int comparison
          assert.GreaterT(t, count, 0)              // numeric comparison
          assert.ElementsMatchT(t, expected, actual) // slice comparison
  2. You want refactoring safety - Compiler catches broken tests immediately

    // If getUserIDs() changes from []int to []string,
          // the compiler flags this line immediately
          assert.ElementsMatchT(t, expectedIDs, getUserIDs())
  3. IDE assistance matters - Autocomplete suggests only correctly-typed variables

    // Typing: assert.EqualT(t, expectedUser, actual
    //                                              ^
    // IDE suggests: actualUser ✓  (correct type)
    //               actualOrder ✗ (wrong type - grayed out)
  4. Performance-critical tests - See benchmarks for 1.2-81x speedups

🔄 Use Reflection Variants (no suffix) When:

  1. Intentionally comparing different types - Especially with EqualValues

    // Comparing int and int64 for semantic equality
          assert.EqualValues(t, int64(42), int32(42))  // ✓ Reflection handles this
          assert.EqualT(t, int64(42), int32(42))       // ❌ Compiler error
  2. Working with heterogeneous collections - []any or interface{} slices

    mixed := []any{1, "string", true}
          assert.Contains(t, mixed, "string")  // ✓ Reflection works
  3. Dynamic type scenarios - Where compile-time type is unknown

    var result interface{} = getResult()
          assert.Equal(t, expected, result)  // ✓ Reflection handles dynamic types
  4. Backward compatibility - Existing test code using reflection-based assertions

Type Safety Benefits

Catching Refactoring Errors

Generic assertions act as a safety net during refactoring:

  • Without Generics ❌
    // Original code
    type UserID int
    var userIDs []UserID
    
    assert.ElementsMatch(t, userIDs, getActiveUsers())
    
    // Later: UserID changes to string
    type UserID string
    var userIDs []UserID
    
    // Test still compiles!
    // Fails mysteriously at runtime or passes with wrong comparison
    assert.ElementsMatch(t, userIDs, getActiveUsers())
  • With Generics ✅
    // Original code
    type UserID int
    var userIDs []UserID
    
    assert.ElementsMatchT(t, userIDs, getActiveUsers())
    
    // Later: UserID changes to string
    type UserID string
    var userIDs []UserID
    
    // Compiler immediately flags the error
    assert.ElementsMatchT(t, userIDs, getActiveUsers())
    // ❌ Compile error: type mismatch!

Preventing Wrong Comparisons

Generic assertions force you to think about what you’re comparing:

  • Pointer vs Value Comparison
    expected := &User{ID: 1, Name: "Alice"}
    actual := &User{ID: 1, Name: "Alice"}
    
    // Reflection: Compares pointer addresses (probably wrong)
    assert.Equal(t, expected, actual)  // ✗ Fails (different addresses)
    
    // Generic: Makes the intent explicit
    assert.EqualT(t, expected, actual)   // Compares pointers
    assert.EqualT(t, *expected, *actual) // Compares values ✓
  • Type Confusion Prevention
    userID := 42
    orderID := "ORD-123"
    
    // Reflection: Compiles, wrong comparison
    assert.Equal(t, userID, orderID)  // Runtime failure
    
    // Generic: Compiler catches the mistake
    assert.EqualT(t, userID, orderID)  // ❌ Compile error!

Available Generic Functions

Testify v2 provides generic variants across all major domains:

Equality (4 functions)

  • EqualT[V comparable] - Type-safe equality for comparable types
  • NotEqualT[V comparable] - Type-safe inequality
  • SameT[V comparable] - Pointer identity check
  • NotSameT[V comparable] - Different pointer check

Comparison (6 functions)

  • GreaterT[V Ordered] - Type-safe greater-than comparison
  • GreaterOrEqualT[V Ordered] - Type-safe >=
  • LessT[V Ordered] - Type-safe less-than comparison
  • LessOrEqualT[V Ordered] - Type-safe <=
  • PositiveT[V SignedNumeric] - Assert value > 0
  • NegativeT[V SignedNumeric] - Assert value < 0

Collection (12 functions)

  • StringContainsT[S Text] - String/byte slice contains substring
  • SliceContainsT[E comparable] - Slice contains element
  • MapContainsT[K comparable, V any] - Map contains key
  • SeqContainsT[E comparable] - Iterator contains element (Go 1.23+)
  • ElementsMatchT[E comparable] - Slices have same elements (any order)
  • SliceSubsetT[E comparable] - Slice is subset of another
  • Plus negative variants: *NotContainsT, NotElementsMatchT, SliceNotSubsetT

Ordering (6 functions)

  • IsIncreasingT[E Ordered] - Slice elements strictly increasing
  • IsDecreasingT[E Ordered] - Slice elements strictly decreasing
  • IsNonIncreasingT[E Ordered] - Slice elements non-increasing (allows equal)
  • IsNonDecreasingT[E Ordered] - Slice elements non-decreasing (allows equal)
  • SortedT[E Ordered] - Slice is sorted (generic-only function)
  • NotSortedT[E Ordered] - Slice is not sorted (generic-only function)

Numeric (2 functions)

  • InDeltaT[V Measurable] - Numeric comparison with absolute delta (supports integers and floats)
  • InEpsilonT[V Measurable] - Numeric comparison with relative epsilon (supports integers and floats)

Boolean (2 functions)

  • TrueT[B Boolean] - Assert boolean is true
  • FalseT[B Boolean] - Assert boolean is false

String (2 functions)

  • RegexpT[S Text] - String matches regex (string or []byte)
  • NotRegexpT[S Text] - String doesn’t match regex

Type (2 functions)

  • IsOfTypeT[EType any] - Assert value is of type EType (no dummy value needed!)
  • IsNotOfTypeT[EType any] - Assert value is not of type EType

JSON & YAML (2 functions)

  • JSONEqT[S Text] - JSON strings are semantically equal
  • YAMLEqT[S Text] - YAML strings are semantically equal
See Complete API

For a detailed documentation of all generic functions, see the API Reference organized by domain.

Practical Examples

Example 1: Collection Testing

  • Type-Safe Collection Assertions
    import (
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func TestUserPermissions(t *testing.T) {
    	user := getUser(123)
    
    	expectedPerms := []string{"read", "write"}
    	actualPerms := user.Permissions
    
    	// Compiler ensures both slices are []string
    	assert.ElementsMatchT(t, expectedPerms, actualPerms)
    
    	// Check subset relationship
    	assert.SliceSubsetT(t, []string{"read"}, actualPerms)
    }
  • Iterator Support (Go 1.23+)
    import (
    	"slices"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func TestSequenceContains(t *testing.T) {
    	// iter.Seq[int] from Go 1.23
    	numbers := slices.Values([]int{1, 2, 3, 4, 5})
    
    	// Type-safe iterator checking
    	assert.SeqContainsT(t, numbers, 3)
    	assert.SeqNotContainsT(t, numbers, 99)
    }

Example 2: Numeric Comparisons

  • Ordered Types
    import (
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func TestPricing(t *testing.T) {
    	price := calculatePrice(item)
    	discount := calculateDiscount(item)
    
    	// Type-safe numeric comparisons
    	assert.PositiveT(t, price)
    	assert.GreaterT(t, price, discount)
    	assert.LessOrEqualT(t, discount, price)
    }
  • Float Comparisons
    import (
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func TestPhysicsCalculation(t *testing.T) {
    	result := calculateVelocity(mass, force)
    	expected := 42.0
    
    	// Type-safe float comparison with delta
    	assert.InDeltaT(t, expected, result, 1e-6)
    
    	// Or with epsilon (relative error)
    	assert.InEpsilonT(t, expected, result, 0.001)
    }

Example 3: Type Checking Without Dummy Values

The IsOfTypeT function eliminates the need for dummy values:

  • Old Way (Reflection)
    import (
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func TestGetUser(t *testing.T) {
    	result := getUser(123)
    
    	// Need to create a dummy User instance
    	assert.IsType(t, User{}, result)
    
    	// Or use a pointer dummy
    	assert.IsType(t, (*User)(nil), result)
    }
  • New Way (Generic)
    import (
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func TestGetUser(t *testing.T) {
    	result := getUser(123)
    
    	// No dummy value needed!
    	assert.IsOfTypeT[User](t, result)
    
    	// For pointer types
    	assert.IsOfTypeT[*User](t, result)
    }

Example 4: Sorting and Ordering

  • Ordering Checks
    import (
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func TestSortedData(t *testing.T) {
    	timestamps := []int64{
    		1640000000,
    		1640000100,
    		1640000200,
    	}
    
    	// Type-safe ordering assertions
    	assert.IsIncreasingT(t, timestamps)
    	assert.SortedT(t, timestamps) // Generic-only function
    }
  • Custom Ordered Types
    import (
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    type Priority int
    
    const (
    	Low Priority = iota
    	Medium
    	High
    )
    
    func TestPriorities(t *testing.T) {
    	tasks := []Priority{Low, Medium, High}
    
    	// Works with Ordered types (custom types supported)
    	assert.IsNonDecreasingT(t, tasks)
    }

Migration Guide

Step 1: Identify High-Value Targets

Start with the most common assertions that benefit most from type safety:

// High value: Collection operations (also get big performance wins)
assert.Equal  assert.EqualT
assert.ElementsMatch  assert.ElementsMatchT
assert.Contains  assert.ContainsT (SliceContainsT/MapContainsT/StringContainsT)

// High value: Comparisons (eliminate allocations)
assert.Greater  assert.GreaterT
assert.Less  assert.LessT
assert.Positive  assert.PositiveT

// High value: Type checks (cleaner API)
assert.IsType(t, User{}, v)  assert.IsOfTypeT[User](t, v)

Step 2: Automated Search & Replace

Use your IDE or tools to find and replace systematically:

# Find all Equal assertions
grep -r "assert\.Equal(" . --include="*_test.go"

# Find all require.Greater assertions
grep -r "require\.Greater(" . --include="*_test.go"

Step 3: Fix Compiler Errors

The compiler will catch type mismatches. This is a feature, not a bug:

  • Compiler Error
    // Original code
    assert.EqualT(t, int64(result), count)
    // ❌ Error: mismatched types int64 and int
  • Fix Option 1: Same Type
    // Convert to same type
    assert.EqualT(t, int64(result), int64(count))
  • Fix Option 2: Use Reflection
    // If cross-type comparison is intentional
    assert.Equal(t, int64(result), count)

Step 4: Incremental Adoption

You don’t need to migrate everything at once:

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestMixedAssertions(t *testing.T) {
	// Use generic where types are known
	assert.EqualT(t, 42, getAge())
	assert.GreaterT(t, count, 0)

	// Keep reflection for dynamic types
	var result interface{} = getResult()
	assert.Equal(t, expected, result)

	// Both styles coexist peacefully
}

Performance Benefits

Generic assertions provide significant performance improvements, especially for collection operations:

OperationSpeedupWhen It Matters
ElementsMatchT21-81x fasterLarge collections, hot test paths
EqualT10-13x fasterMost common assertion
GreaterT/LessT10-22x fasterNumeric comparisons
SliceContainsT16x fasterCollection membership tests
Learn More

See the complete Performance Benchmarks for detailed analysis and real benchmark results.

Best Practices

✅ Do

  1. Prefer generic variants by default - Type safety is always valuable

    assert.EqualT(t, expected, actual)  // ✓ Type safe
  2. Let the compiler guide you - Type errors reveal design issues

    // Compiler error reveals you're comparing wrong types
          assert.EqualT(t, userID, orderID)  // ❌ Good - catches mistake!
  3. Use explicit types for clarity

    assert.IsOfTypeT[*User](t, result)  // ✓ Clear intent
  4. Leverage performance wins in hot paths - Generic assertions are faster

    // Table-driven tests with many iterations
          for _, tc := range testCases {
              assert.EqualT(t, tc.expected, tc.actual)  // ✓ Fast
          }

❌ Don’t

  1. Don’t force generics for dynamic types

    var result interface{} = getResult()
          assert.Equal(t, expected, result)  // ✓ Reflection is fine here
  2. Don’t use reflection to avoid fixing types

    // Bad: Using reflection to bypass type safety
          assert.Equal(t, expected, actual)  // ✗ Defeats the purpose
    
          // Good: Fix the types or use EqualValues if intentional
          assert.EqualT(t, expected, actual)  // ✓ Type safe
  3. Don’t create unnecessary type conversions

    // Bad: Unnecessary conversion
          assert.EqualT(t, int64(42), int64(result))
    
          // Good: Work with natural types
          assert.EqualT(t, 42, result)

Type Constraints Reference

Generic assertions use custom type constraints defined in internal/assertions/generics.go:

ConstraintDefinitionDescriptionExample Types
comparableGo built-inTypes that support == and !=int, string, bool, pointers, structs (if all fields are comparable)
Boolean~boolBoolean and named bool typesbool, type MyBool bool
Text~string | ~[]byteString or byte slice typesstring, []byte, custom string/byte types
Orderedcmp.Ordered | []byte | time.TimeExtends cmp.Ordered with byte slices and timeStandard ordered types plus []byte and time.Time
SignedNumeric~int... | ~float32 | ~float64Signed integers and floatsint, int8-int64, float32, float64
UnsignedNumeric~uint...Unsigned integersuint, uint8-uint64
MeasurableSignedNumeric | UnsignedNumericAll numeric types (for delta comparisons)Used by InDeltaT/InEpsilonT - supports integers AND floats
RegExpText | *regexp.RegexpRegex pattern or compiled regexpstring, []byte, *regexp.Regexp
Key Differences from Standard Go Constraints
  • Ordered is extended: Adds []byte and time.Time to cmp.Ordered for seamless bytes.Compare() and time.Time.Compare() support
  • Measurable supports integers: InDeltaT and InEpsilonT work with both integers and floats, not just floating-point types
  • Custom type support: All constraints use the ~ operator to support custom types (e.g., type UserID int)

Summary

Generic assertions in testify v2 provide:

Type Safety: Catch errors when writing tests, not when running them ✅ Performance: 1.2x to 81x faster than reflection-based assertions ✅ Better IDE Support: Autocomplete suggests correctly-typed values ✅ Refactoring Safety: Compiler catches broken tests immediately ✅ Zero Downside: Always as fast or faster than reflection variants

Start using generic assertions today - add the T suffix to your existing assertions and let the compiler guide you to better, safer tests.


Quick Reference
  • Generic functions: Add T suffix (e.g., EqualT, GreaterT, ElementsMatchT)
  • Format variants: Add Tf suffix (e.g., EqualTf, GreaterTf)
  • When to use: Prefer generics for known concrete types
  • When not to: Keep reflection for dynamic types and cross-type comparisons
  • Performance: See benchmarks for dramatic speedups

See Also

  • Examples - Practical examples including generic assertion usage
  • Tutorial - Best practices for writing tests with testify
  • Usage Guide - API conventions and naming patterns
  • Performance Benchmarks - Detailed performance analysis of generic vs reflection
  • Changes from v1 - Complete list of new generic functions and improvements
  • API Reference - Complete assertion catalog with all generic variants
Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Changes from v1

Summary

Key Changes:

  • Zero Dependencies: Completely self-contained
  • New functions: 58 additional assertions (43 generic + 15 reflection-based)
  • Performance: ~10x for generic variants (from 1.2x to 81x, your mileage may vary)
  • 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

  • Type Safety: generic assertions catch errors at compile time
  • Documentation: compelling documentation site to search the API by use-case domain
  • Maintainability: 100% code generation from single source
  • Quality: 96% test coverage, use unified test scenarios, extensive fuzzing & benchmarking

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

The original philosophy of testify is preserved, and the new API is 90% compatible.

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 (840 functions from 127 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. See Benchmarks


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

Behavior changes: None

Condition

New Function (1)

FunctionType ParametersDescription
Consistently[C Conditioner]func() bool or func(context.Context) errorasync assertion to express “always true” (adapted proposal #1606, #1087)

⚠️ Behavior Changes

ChangeOriginDescription
Fixed goroutine leak#1611Consolidated Eventually, Never, and EventuallyWith 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
func(context.Context) error conditionsextensions to the async domaincontrol over context allows for more complex cases to be supported
Type parameterInternal refactoringEventually now accepts several signatures for its condition and uses a type parameter (non-breaking)

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.

Breaking Changes

ChangeOriginDescription
RenamingInternal refactortingEventuallyWithT renamed into EventuallyWith (conflicted with the convention adopted for generics)
RemovalAPI simplificationComparison type is removed as a mere alias to func() bool

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 Functions (3)

FunctionType ParametersDescription
JSONEqT[S Text]String or []byteType-safe JSON semantic equality
JSONMarshalAsT[EDoc Text]String or []byteType-safe JSON marshal and equality check
JSONUnmarshalAsT[ADoc Text, Object any]String or []byteType-safe JSON unmarshal and equality check

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

Removed extraneous type declaration PanicTestFunc (func()).

Safety

New domain for resource leak detection.

FunctionTypeDescription
NoGoRoutineLeakReflectionAssert that no goroutines leak from a tested function
NoFileDescriptorLeakReflectionAssert that no file descriptors leak from a tested function (Linux)

Implementation

NoGoRoutineLeak uses pprof labels instead of stack-trace heuristics (like go.uber.org/goleak):

  • Only goroutines spawned by the tested function are checked
  • Pre-existing goroutines (runtime, pools, parallel tests) are ignored automatically
  • No configuration or filter lists needed
  • Works safely with t.Parallel()

NoFileDescriptorLeak compares open file descriptors before and after the tested function (Linux only, via /proc/self/fd).

See Examples for usage patterns.

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 Functions (3)

FunctionType ParametersDescription
YAMLEqT[S Text]String or []byteType-safe YAML semantic equality
YAMLMarshalAsT[EDoc Text]String or []byteType-safe YAML marshal and equality check
YAMLUnmarshalAsT[ADoc Text, Object any]String or []byteType-safe YAML unmarshal and equality check

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/ (~6,000 LOC)
  • Generated: ~800+ 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 (19 domains)
  • Comprehensive examples and tutorials
  • Performance benchmarks

Project Metrics

MetricValue
New functions58 (43 generic + 15 reflection)
Total assertions127 base assertions
Generated functions840 (see the maths)
Generic coverage10 domains (10/19)
Performance improvement1.2x to 81x faster
Dependencies0 external (was 2 required)
Test coverage96% overall, 99% on public APIs
Documentation domains19 logical categories

See Also

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Upstream Tracking

Upstream Tracking

We continue to monitor and selectively adopt changes from the upstream repository.

Implemented from Upstream

Monitoring

  • 🔍 #1601 - NoFieldIsZero
  • 🔍 #1840 - JSON presence check without exact values

Superseded by Our Implementation

  • #1845 - Fix Eventually/Never regression (superseded by context-based pollCondition)
  • #1830 - CollectT.Halt() (superseded by context-based pollCondition)
  • #1819 - Handle unexpected exits (superseded by context-based pollCondition)
  • #1824 - Spew testing (superseded by property-based fuzzing)

Review frequency: Quarterly (next review: May 2026)


Appendix: Upstream References

This table catalogs all upstream PRs and issues from github.com/stretchr/testify that we have processed.

Implemented (Adapted or Merged)

ReferenceTypeSummaryOutcome in Fork
#994PRColorize expected vs actual values✅ Adapted into enable/color module with themes and configuration
#1223PRDisplay uint values in decimal instead of hex✅ Merged - Applied to diff output
#1232PRColorized output for expected/actual/errors✅ Adapted into enable/color module
#1356PRpanic(nil) handling for Go 1.21+✅ Merged - Updated panic assertions
#1467PRColorized output with terminal detection✅ Adapted into enable/color module (most mature implementation)
#1480PRColorized diffs via TESTIFY_COLORED_DIFF env var✅ Adapted with env var support in enable/color
#1513PRJSONEqBytes for byte slice JSON comparison✅ Merged - Added to JSON domain
#1685PRIterator support (iter.Seq) for Contains/ElementsMatch✅ Partial - Implemented SeqContainsT and SeqNotContainsT only
#1772PRYAML library migration to maintained fork✅ Adapted - Used gopkg.in/yaml.v3 in optional enable/yaml module
#1797PRCodegen package consolidation and licensing✅ Adapted - Complete rewrite of code generation system
#1803PRKind/NotKind assertions✅ Merged - Added to Type domain
#1805IssueGeneric IsOfType[T]() without dummy value✅ Implemented - IsOfTypeT and IsNotOfTypeT in Type domain
#1816IssueFix panic on unexported struct key in map✅ Fixed in internalized go-spew
#1818PRFix panic on invalid regex in Regexp/NotRegexp✅ Merged - Added graceful error handling
#1822IssueDeterministic map ordering in diffs✅ Fixed in internalized go-spew
#1825PRFix panic using EqualValues with uncomparable types✅ Merged - Enhanced type safety in EqualValues
#1826IssueType safety with spew (meta-issue)✅ Addressed through comprehensive fuzzing and fixes
#1828PRFixed panic with unexported fields in maps✅ Merged into internalized go-spew
#1829IssueFix time.Time rendering in diffs✅ Fixed in internalized go-spew
#1611IssueGoroutine leak in Eventually/Never✅ Fixed by using context.Context (consolidation into single pollCondition function)
#1813IssuePanic with unexported fields✅ Fixed via #1828 in internalized spew
#1087IssueConsistently assertion✅ Adapted
#1606PRConsistently assertion✅ Adapted

Superseded by Our Implementation

ReferenceTypeSummaryWhy Superseded
#1845PRFix Eventually/Never regressionSuperseded by context-based pollCondition implementation (we don’t have this bug)
#1819PRHandle unexpected exits in EventuallySuperseded by context-based pollCondition implementation
#1824PRSpew testing improvementsSuperseded by property-based fuzzing with random type generator
#1830PRCollectT.Halt() for stopping testsSuperseded by context-based pollCondition implementation

Under Consideration (Monitoring)

ReferenceTypeSummaryStatus
#1601IssueNoFieldIsZero assertion🔍 Monitoring - Considering implementation
#1840IssueJSON presence check without exact values🔍 Monitoring - Interesting for testing APIs with generated IDs

Informational (Not Implemented)

ReferenceTypeSummaryOutcome
#1147IssueGeneral discussion about generics adoptionℹ️ Marked “Not Planned” upstream - We implemented our own generics approach (42 functions)
#1308PRComprehensive refactor with generic type parametersℹ️ Draft for v2.0.0 upstream - We took a different approach with the same objective

Summary Statistics

CategoryCount
Implemented/Merged23
Superseded4
Monitoring2
Informational2
Total Processed31

Note: This fork maintains an active relationship with upstream, regularly reviewing new PRs and issues. The quarterly review process ensures we stay informed about upstream developments while maintaining our architectural independence.


See Also


Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Migration Guide

Migration Guide from stretchr/testify v1

This guide covers migrating from stretchr/testify to go-openapi/testify/v2. You can use the automated migration tool or migrate manually.

Automated Migration Tool

migrate-testify automates both the import migration (pass 1) and the generic upgrade (pass 2). It uses go/packages and go/types for type-checked, semantics-preserving transformations.

Installation

go install github.com/go-openapi/testify/hack/migrate-testify/v2@latest

This installs the migrate-testify binary into your $GOBIN.

Quick Start

# Run both passes on the current directory (preview first, then apply)
migrate-testify --all --dry-run .
migrate-testify --all .

# Or run each pass separately
migrate-testify --migrate .
migrate-testify --upgrade-generics .

Pass 1: Import Migration (--migrate)

Rewrites stretchr/testify imports to go-openapi/testify/v2:

# Dry-run to preview changes
migrate-testify --migrate --dry-run .

# Apply changes
migrate-testify --migrate .

This pass handles:

  • Import path rewriting (assert, require, root package)
  • Function renames (EventuallyWithT to EventuallyWith, NoDirExists to DirNotExists, etc.)
  • Type replacement (PanicTestFunc to func())
  • YAML enable import injection (adds _ "github.com/go-openapi/testify/v2/enable/yaml" when YAMLEq is used)
  • Incompatible import detection (mock, suite, http packages emit warnings with guidance)
  • go.mod update (drops stretchr/testify, adds go-openapi/testify/v2)

Pass 2: Generic Upgrade (--upgrade-generics)

Upgrades reflection-based assertions to generic variants where types are statically resolvable and the semantics are preserved:

# Dry-run to preview changes
migrate-testify --upgrade-generics --dry-run .

# Apply changes
migrate-testify --upgrade-generics .

The tool is conservative: it only upgrades when:

  • Argument types are statically known (no any, no interface{})
  • Types satisfy the required constraint (comparable, Ordered, Text, etc.)
  • For Equal/NotEqual: types are “deeply comparable” (no pointers or structs with pointer fields)
  • For Contains: the container type disambiguates to StringContainsT, SliceContainsT, or MapContainsT
  • IsType is flagged for manual review (argument count changes)

Assertions that cannot be safely upgraded are tracked and reported in the summary with a specific reason (e.g., “pointer type”, “interface{}/any”, “type mismatch”). Use --verbose to see the file and line of each skipped assertion.

Reference

Usage: migrate-testify [flags] [directory]

Migrate stretchr/testify to go-openapi/testify/v2 and upgrade to generic assertions.

Flags:
  -all                Run both passes sequentially
  -dry-run            Show diffs without modifying files
  -migrate            Run pass 1: stretchr/testify -> go-openapi/testify/v2
  -upgrade-generics   Run pass 2: reflection -> generic assertions
  -verbose            Print detailed transformation info
  -skip-gomod         Skip go.mod changes
  -skip-vendor        Skip vendor/ directory (default true)
  -version string     Target testify version (default "v2.3.0")

At least one of --migrate, --upgrade-generics, or --all is required.

Mono-repo support:
  Pass 1 walks the filesystem and works across module boundaries.
  Pass 2 requires type information and uses go/packages to load code.
  For multi-module repos, a go.work file must be present so that pass 2
  can load all workspace modules. Create one with:
    go work init . ./sub/module1 ./sub/module2 ...

Post-migration checklist:
  - Run your linter: the migration may surface pre-existing unchecked linting issues.
  - Run your test suite to verify all tests still pass.

Manual Migration

1. Update Import Paths

// Old
import "github.com/stretchr/testify/assert"
import "github.com/stretchr/testify/require"

// New
import "github.com/go-openapi/testify/v2/assert"
import "github.com/go-openapi/testify/v2/require"

2. Optional: Enable YAML Support

If you use YAMLEq assertions: this feature is now opt-in.

import _ "github.com/go-openapi/testify/enable/yaml/v2"

Without this import, YAML assertions will panic with a helpful error message.

3. Optional: Enable Colorized Output

import _ "github.com/go-openapi/testify/enable/colors/v2"

Use go additional test flags or environment variables: TESTIFY_COLORIZED=true, TESTIFY_THEME=dark|light

Example:

go test -v -testify.colorized -testify.theme=light .

Colorized Test

4. Optional: Adopt Generic Assertions

For better type safety and performance, consider migrating to generic assertion variants. This is entirely optional: reflection-based assertions continue to work as before.

Identify Generic-Capable Assertions

Look for these common assertions in your tests:

// Equality
assert.Equal  assert.EqualT
assert.NotEqual  assert.NotEqualT

// Comparisons
assert.Greater  assert.GreaterT
assert.Less  assert.LessT
assert.Positive  assert.PositiveT
assert.Negative  assert.NegativeT

// Collections
assert.Contains  assert.ContainsT (or SliceContainsT/MapContainsT/StringContainsT)
assert.ElementsMatch  assert.ElementsMatchT
assert.Subset  assert.SubsetT

// Ordering
assert.IsIncreasing  assert.IsIncreasingT
assert.IsDecreasing  assert.IsDecreasingT

// Type checks
assert.IsType(t, User{}, v)  assert.IsOfTypeT[User](t, v)  // No dummy value!

Simply add T to the function name. The compiler will check types automatically:

// Before
assert.Equal(t, expected, actual)
assert.ElementsMatch(t, slice1, slice2)

// After
assert.EqualT(t, expected, actual)
assert.ElementsMatchT(t, slice1, slice2)
Fix Type Mismatches

The compiler will now catch type errors. This is a feature—it reveals bugs:

// Compiler catches this
assert.EqualT(t, int64(42), int32(42))
// Error: mismatched types int64 and int32

// Fix: Use same type
assert.EqualT(t, int64(42), int64(actual))

// Or: Use reflection if cross-type comparison is intentional
assert.Equal(t, int64(42), int32(42))  // Still works
Pointer Semantics: When NOT to Upgrade

Generic assertions use Go’s == operator, while reflection-based assertions use reflect.DeepEqual. For most types these are equivalent, but they differ for pointers and structs containing pointers:

a := &MyStruct{Name: "alice"}
b := &MyStruct{Name: "alice"}

assert.Equal(t, a, b)   // PASSES (reflect.DeepEqual compares pointed-to values)
assert.EqualT(t, a, b)  // FAILS  (== compares pointer addresses)

Do not upgrade to generic variants when:

  • Arguments are pointer types (*T) — EqualT compares addresses, not values
  • Arguments are structs with pointer fields — == compares field addresses, DeepEqual compares field values
  • You intentionally rely on cross-type comparison (int64 vs int32)

The automated migration tool handles this automatically by only upgrading assertions where the argument types are “deeply comparable” — types where == and reflect.DeepEqual produce the same result.

Benefits of Generic Assertions
  • Compile-time type safety: Catch errors when writing tests
  • Performance: 1.2x to 81x faster (see Benchmarks)
  • IDE support: Better autocomplete with type constraints
  • Refactoring safety: Type changes break tests at compile time, not runtime

See the Generics Guide for detailed usage patterns and best practices.

5. Remove Suite/Mock Usage

Replace testify mocks with:

  • mockery for mocking Replace testify suites with:
  • Standard Go subtests for test organization
  • or wait until we reintroduce this feature (possible, but not certain)

6. Replace go.uber.org/goleak with NoGoRoutineLeak

If you use go.uber.org/goleak to detect goroutine leaks in tests, consider replacing it with assert.NoGoRoutineLeak (or require.NoGoRoutineLeak), which is built into testify v2.

// Before (with goleak)
import "go.uber.org/goleak"

func TestNoLeak(t *testing.T) {
	defer goleak.VerifyNone(t)
	// ... test code ...
}

// After (with testify v2)
import "github.com/go-openapi/testify/v2/assert"

func TestNoLeak(t *testing.T) {
	assert.NoGoRoutineLeak(t, func() {
		// ... test code ...
	})
}

This removes the go.uber.org/goleak dependency. This step is not automated by the migration tool.

7. Remove use of the testify/http package

If you were still using the deprecated package github.com/stretchr/testitfy/http, you’ll need to replace it by the standard net/http/httptest package.

We won’t reintroduce this package ever.


Breaking Changes Summary

Removed Packages

  • suite - Use standard Go subtests
  • mock - Use mockery
  • http - May be reintroduced later

Removed Functions and Types

  • All deprecated functions from v1 removed
  • Removed extraneous “helper” types: PanicTestFunc (func())

Behavior Changes

Make sure to check the behavior changes as we have fixed a few quirks in the existing API (mostly edge cases handling).


See Also

  • Changes from v1 - Complete list of all changes, fixes, and new features
  • Examples - Practical examples showing v2 usage patterns
  • Generics Guide - Learn about the 43 new type-safe generic assertions
  • Usage Guide - API conventions and how to navigate the documentation
  • Tutorial - Best practices for writing tests with testify v2
Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Project Documentation

Useful guides for contributors and maintainers.


Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Subsections of Project Documentation

README

The v2 our tests wanted

Motivation

See why we wanted a v2.

Approach with this fork

This fork targets go1.24.

  • zero external dependencies by default
  • extra features (and dependencies) are opt-in
  • modernized code base
  • simplified maintenance
  • can add or remove assertions with ease
  • mitigated API bloat with comprehensive domain-indexed documentation
  • can leverage generics without backward compatibility concerns

The approach will be selective and pragmatic rather than comprehensive:

  • Targeted improvements where generics provide clear value without compromising existing functionality
  • Focus on eliminating anti-patterns like dummy value instantiation in IsType (see #1805)
  • Preserve reflection-based flexibility for comparing complex types rather than forcing everything through generic constraints
  • Careful constraint design to ensure type safety without being overly restrictive or permissive

The goal is to enhance type safety and developer experience where it matters most, while maintaining the flexibility that makes testify useful for real-world testing scenarios.

Breaking changes from v1

  • YAMLEq panics by default: must enable the feature with an additional blank import
  • deprecated types and methods have been removed
  • removed the suite, mocks and http packages
  • replaced internal utility package _codegen by codegen

See all changes from v1 and check out our ROADMAP.

API Stability Guarantee

The assertions currently used by go-openapi projects constitute our stable API. These entry points will remain backward compatible. Other assertions may evolve as we refine the v2 API.


See Also

Getting Started:

Project Documentation:


Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Contributing to Testify v2

How to contribute to https://github.com/go-openapi/testify?

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Subsections of Contributing to Testify v2

Contributing Guidelines

You’ll find here general guidelines to contribute to this project. They mostly correspond to standard practices for open source repositories.

We have tried to keep things as simple as possible.

TL;DR

If you’re an experienced go developer on github, then you should just feel at home with us and you may well skip the rest of this document.

You’ll essentially apply the usual guidelines for a go library project on github.

These guidelines are common to all libraries published on github by the go-openapi organization, so you’ll feel at home with any of our projects.

You’ll find more detailed (or repo-specific) instructions in the maintainer’s docs.

How can I contribute?

There are many ways in which you can contribute, not just code. Here are a few ideas:

  • Reporting issues or bugs
  • Suggesting improvements
  • Documentation
  • Art work that makes the project look great
  • Code
    • proposing bug fixes and new features that are within the main project scope
    • improving test coverage
    • addressing code quality issues

Questions & issues

Asking a question

You may inquire anything about this library by reporting a “Question” issue on github.

You may also join our discord server where you may discuss issues or requests.

Discord Server

Reporting issues

Reporting a problem with our libraries is a valuable contribution. You can do this on the github issues page of this repository.

Please be as specific as possible when describing your issue.

Whenever relevant, please provide information about your environment (go version, OS).

Adding a code snippet to reproduce the issue is great, and a big time saver for maintainers.

Triaging issues

You can help triage issues which may include:

  • reproducing bug reports
  • asking for important information, such as version numbers or reproduction instructions
  • answering questions and sharing your insight in issue comments

Code contributions

Pull requests are always welcome

We are always thrilled to receive pull requests, and we do our best to process them as fast as possible.

Not sure if that typo is worth a pull request? Do it! We will appreciate it.

If your pull request is not accepted on the first try, don’t be discouraged! If there’s a problem with the implementation, hopefully you’ve received feedback on what to improve.

If you have a lot of ideas or a lot of issues to solve, try to refrain a bit and post focused pull requests. Think that they must be reviewed by a maintainer and it is easy to lose track of things on big PRs.

We’re trying very hard to keep the go-openapi packages lean and focused.

Together, these packages constitute a toolkit for go developers: it won’t do everything for everybody out of the box, but everybody can use it to do just about everything related to OpenAPI.

This means that we might decide against incorporating a new feature.

However, there might be a way to implement that feature on top of our libraries.

Environment

You just need a go compiler to be installed. No special tools are needed to work with our libraries.

The minimal go compiler version required is always the old stable (latest minor go version - 1).

Our libraries are designed and tested to work on Linux, MacOS and Windows.

If you’re used to work with go you should already have everything in place.

Although not required, you’ll be certainly more productive with a local installation of golangci-lint, the meta-linter our CI uses.

If you don’t have it, you may install it like so:

go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest

Conventions

Git flow

Fork the repo and make changes to your fork in a feature branch.

To submit a pull request, push your branch to your fork (e.g. upstream remote): github will propose to open a pull request on the original repository.

Typically you’d follow some common naming conventions:

  • if it’s a bug fixing branch, name it fix/XXX-something where XXX is the number of the issue on github
  • if it’s a feature branch, create an enhancement issue to announce your intentions, and name it feature/XXX-something where XXX is the number of the issue.

NOTE: we don’t enforce naming conventions on branches: it’s your fork after all.

Tests

Submit unit tests for your changes.

Go has a great built-in test framework ; use it!

Take a look at existing tests for inspiration, and run the full test suite on your branch before submitting a pull request.

Our CI measures test coverage and the test coverage of every patch.

Although not a blocking step - because there are so many special cases - this is an indicator that maintainers consider when approving a PR. Please try your best to cover at least 80% of your patch.

Code style

You may read our stance on code style there.

Documentation

Don’t forget to update the documentation when creating or modifying a feature.

Most documentation for this library is directly found in code as comments for godoc.

The documentation for this go-openapi package is published on the public go docs site.


Check your documentation changes for clarity, concision, and correctness.

If you want to assess the rendering of your changes when published to pkg.go.dev, you may want to install the pkgsite tool proposed by golang.org.

go install golang.org/x/pkgsite/cmd/pkgsite@latest

Then run on the repository folder:

pkgsite .

This will run a godoc server locally where you may see the documentation generated from your local repository.

Commit messages

Pull requests descriptions should be as clear as possible and include a reference to all the issues that they address.

Pull requests must not contain commits from other users or branches.

Commit messages are not required to follow the “conventional commit” rule, but it’s certainly a good thing to follow that convention (e.g. “fix: fixed panic in XYZ”, “ci: did this”, “feat: did that” …).

The title in your commit message is used directly to produce our release notes: try to keep them neat.

The commit message body should detail your changes.

If an issue should be closed by a commit, please add this reference in the commit body:

* fixes #{issue number}

Code review

Code review comments may be added to your pull request.

Discuss, then make the suggested modifications and push additional commits to your feature branch.

Be sure to post a comment after pushing. The new commits will show up in the pull request automatically, but the reviewers will not be notified unless you comment.

Before the pull request is merged, make sure that you’ve squashed your commits into logical units of work using git rebase -i and git push -f.

After every commit the test suite should be passing.

Include documentation changes in the same commit so that a revert would remove all traces of the feature or fix.

Sign your work

Software is developped by real people.

The sign-off is a simple line at the end of your commit message, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch.

We require the simple DCO below with an email signing your commit. PGP-signed commit are greatly appreciated but not required.

The rules are pretty simple:

using your real name (sorry, no pseudonyms or anonymous contributions.)

You can add the sign-off when creating the git commit via git commit -s.

Code contributions by AI agents

Our agentic friends are welcome to contribute!

We only have a few demands to keep-up with human maintainers.

  1. Issues and PRs written or posted by agents should always mention the original (human) poster for reference
  2. We don’t accept PRs attributed to agents. We don’t want commits signed like “author: @claude.code”. Agents or bots may coauthor commits, though.
  3. Security vulnerability reports by agents should always be reported privately and mention the original (human) poster (see also Security Policy).
Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Developer's Certificate of Origin

TL;DR

We welcome this (simple email signature):

Author: Frederic BIDON <fredbi@yahoo.com>
Date:   Mon Dec 15 11:24:43 2025 +0100

    feat: brainstrom RAG-equipped linter
    
    Signed-off-by: Frederic BIDON <fredbi@yahoo.com>

or this (PGP-signed commit):

gpg: Signature made Tue 30 Dec 2025 09:53:48 PM CET
gpg:                using RSA key XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
gpg: Good signature from "Frédéric BIDON <fredbi@yahoo.com>"
Author: Frederic BIDON <fredbi@yahoo.com>
Date:   Tue Dec 30 21:53:48 2025 +0100

    tentative project doc site
    
    Signed-off-by: Frederic BIDON <fredbi@yahoo.com>

From https://developercertificate.org/:

Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Coding Style

Coding style at go-openapi

TL;DR

Let’s be honest: at go-openapi and go-swagger we’ve never been super-strict on code style and linting.

But perhaps now (2025) is the time to adopt a different stance.

Even though our repos have been early adopters of golangci-lint years ago (we used some other metalinter before), our decade-old codebase is only realigned to new rules from time to time.

Now go-openapi and go-swagger together make up a really large codebase, which is taxing to maintain and keep afloat.

Code quality and the harmonization of rules have thus become things that we need now.

Meta-linter

Universally formatted go code promotes ease of writing, reading, and maintenance.

You should run golangci-lint run before committing your changes.

Many editors have plugins that do that automatically.

We use the golangci-lint meta-linter. The configuration lies in .golangci.yml. You may read the linter’s configuration reference for additional reference.

This configuration is essentially the same across all go-openapi projects.

Some projects may require slightly different settings.

Linting rules posture

Thanks to go’s original design, we developers don’t have to waste much time arguing about code figures of style.

However, the number of available linters has been growing to the point that we need to pick a choice.

Our approach: evaluate, don’t consume blindly

As early adopters of golangci-lint (and its predecessors), we’ve watched linting orthodoxy shift back and forth over the years. Patterns that were idiomatic one year get flagged the next; rules that seemed reasonable in isolation produce noise at scale. Conversations with maintainers of other large Go projects confirmed what our own experience taught us: the default linter set is a starting point, not a prescription.

Our stance is deliberate:

  • Start from default: all, then consciously disable what doesn’t earn its keep. This forces us to evaluate every linter and articulate why we reject it — the disabled list is a design rationale, not technical debt.
  • Tune thresholds rather than disable when a linter’s principle is sound but its defaults are too aggressive for a mature codebase.
  • Require justification for every //nolint directive. Each one must carry an inline comment explaining why it’s there. We maintain a full audit in docs/LINTING.md.
  • Prefer disabling a linter over scattering //nolint across the codebase. If a linter produces systematic false positives on patterns we use intentionally, the linter goes — not our code.
  • Keep the configuration consistent across all go-openapi repositories. Per-repo divergence is a maintenance tax we don’t want to pay.

The result is a three-layer defense: the .golangci.yml config as a baseline, //nolint with mandatory justification for the rare exceptions, and docs/LINTING.md as an audit trail. Contributors should read the disabled list as a set of conscious choices, not gaps to fill.

We enable all linters published by golangci-lint by default, then disable a few ones.

Here are the reasons why they are disabled (update: Feb. 2026, golangci-lint v2.8.0).

  disable:
    - depguard              # we don't want to configure rules to constrain import. That's the reviewer's job
    - exhaustruct           # we don't want to configure regexp's to check type name. That's the reviewer's job
    - funlen                # we accept cognitive complexity as a meaningful metric, but function length is relevant
    - godox                 # we don't see any value in forbidding TODO's etc in code
    - nlreturn              # we usually apply this "blank line" rule to make code less compact. We just don't want to enforce it
    - nonamedreturns        # we don't see any valid reason why we couldn't used named returns
    - noinlineerr           # there is no value added forbidding inlined err
    - paralleltest          # we like parallel tests. We just don't want them to be enforced everywhere
    - recvcheck             # we like the idea of having pointer and non-pointer receivers
    - testpackage           # we like test packages. We just don't want them to be enforced everywhere
    - thelper               # too many false positives on test case factories returning func(*testing.T). See note below
    - tparallel             # see paralleltest
    - varnamelen            # sometimes, we like short variables. The linter doesn't catch cases when a short name is good
    - whitespace            # no added value
    - wrapcheck             # although there is some sense with this linter's general idea, it produces too much noise
    - wsl                   # no added value. Noise
    - wsl_v5                # no added value. Noise

As you may see, we agree with the objective of most linters, at least the principle they are supposed to enforce. But all linters do not support fine-grained tuning to tolerate some cases and not some others.

Note on thelper: the only value we needed from this linter was checking for t.Helper() calls inside genuine test helpers. Unfortunately, it produces persistent false positives on test case factories (functions returning func(*testing.T)), which is a pattern we use extensively with our iter.Seq-based table-driven tests. It also enforces naming conventions we don’t subscribe to. The issue has been reported upstream. We prefer disabling it entirely over maintaining //nolint:thelper directives across every test file.

When this is possible, we enable linters with relaxed constraints.

  settings:
    dupl:
      threshold: 200        # in a older code base such as ours, we have to be tolerant with a little redundancy
                            # Hopefully, we'll be able to gradually get rid of those.
    goconst:
      min-len: 2
      min-occurrences: 3
    cyclop:
      max-complexity: 20    # the default is too low for most of our functions. 20 is a nicer trade-off
    gocyclo:
      min-complexity: 20
    exhaustive:             # when using default in switch, this should be good enough
      default-signifies-exhaustive: true
      default-case-required: true
    lll:
      line-length: 180      # we just want to avoid extremely long lines.
                            # It is no big deal if a line or two don't fit on your terminal.

Final note: since we have switched to a forked version of stretchr/testify, we no longer benefit from the great testifylint linter for tests.

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Contributors

TL;DR

Recognition and thanks to all 256 contributors who have helped build testify from its origins to this fork. Complete list of contributors with commit counts and links to their contributions.

Emeritus

We thank these members for their service to this community.

  • Repository: [‘go-openapi/testify’]
Total ContributorsTotal Contributions
2561124
UsernameAll Time Contribution CountAll Commits
@ernesto-jimenez129https://github.com/go-openapi/testify/commits?author=ernesto-jimenez
@brackendawson110https://github.com/go-openapi/testify/commits?author=brackendawson
@dolmen103https://github.com/go-openapi/testify/commits?author=dolmen
@fredbi66https://github.com/go-openapi/testify/commits?author=fredbi
@tylerb64https://github.com/go-openapi/testify/commits?author=tylerb
@boyan-soubachov35https://github.com/go-openapi/testify/commits?author=boyan-soubachov
@leighmcculloch26https://github.com/go-openapi/testify/commits?author=leighmcculloch
@pmezard19https://github.com/go-openapi/testify/commits?author=pmezard
@alexandear18https://github.com/go-openapi/testify/commits?author=alexandear
@nelsam17https://github.com/go-openapi/testify/commits?author=nelsam
@alexpantyukhin16https://github.com/go-openapi/testify/commits?author=alexpantyukhin
@DAddYE14https://github.com/go-openapi/testify/commits?author=DAddYE
@MovieStoreGuy11https://github.com/go-openapi/testify/commits?author=MovieStoreGuy
@mikeauclair11https://github.com/go-openapi/testify/commits?author=mikeauclair
@architagr11https://github.com/go-openapi/testify/commits?author=architagr
@arjunmahishi10https://github.com/go-openapi/testify/commits?author=arjunmahishi
@arjun-19https://github.com/go-openapi/testify/commits?author=arjun-1
@esdrasbeleza9https://github.com/go-openapi/testify/commits?author=esdrasbeleza
@matryer9https://github.com/go-openapi/testify/commits?author=matryer
@ccoVeille8https://github.com/go-openapi/testify/commits?author=ccoVeille
@AdRiley8https://github.com/go-openapi/testify/commits?author=AdRiley
@mvdkleijn8https://github.com/go-openapi/testify/commits?author=mvdkleijn
@linusbarth8https://github.com/go-openapi/testify/commits?author=linusbarth
@craig655358https://github.com/go-openapi/testify/commits?author=craig65535
@muesli7https://github.com/go-openapi/testify/commits?author=muesli
@emou7https://github.com/go-openapi/testify/commits?author=emou
@HaraldNordgren7https://github.com/go-openapi/testify/commits?author=HaraldNordgren
@cszczepaniak7https://github.com/go-openapi/testify/commits?author=cszczepaniak
@Ogoyukin6https://github.com/go-openapi/testify/commits?author=Ogoyukin
@Neokil6https://github.com/go-openapi/testify/commits?author=Neokil
@torkelrogstad6https://github.com/go-openapi/testify/commits?author=torkelrogstad
@gohargasparyan6https://github.com/go-openapi/testify/commits?author=gohargasparyan
@mutaiib5https://github.com/go-openapi/testify/commits?author=mutaiib
@vkryukov5https://github.com/go-openapi/testify/commits?author=vkryukov
@posener5https://github.com/go-openapi/testify/commits?author=posener
@kevinburkesegment5https://github.com/go-openapi/testify/commits?author=kevinburkesegment
@hendrywiranto5https://github.com/go-openapi/testify/commits?author=hendrywiranto
@Saluev5https://github.com/go-openapi/testify/commits?author=Saluev
@vyas-git4https://github.com/go-openapi/testify/commits?author=vyas-git
@tsioftas4https://github.com/go-openapi/testify/commits?author=tsioftas
@viblo4https://github.com/go-openapi/testify/commits?author=viblo
@paulbellamy4https://github.com/go-openapi/testify/commits?author=paulbellamy
@palsivertsen4https://github.com/go-openapi/testify/commits?author=palsivertsen
@olivergondza4https://github.com/go-openapi/testify/commits?author=olivergondza
@mvrahden4https://github.com/go-openapi/testify/commits?author=mvrahden
@npxcomplete4https://github.com/go-openapi/testify/commits?author=npxcomplete
@jveski4https://github.com/go-openapi/testify/commits?author=jveski
@anpez4https://github.com/go-openapi/testify/commits?author=anpez
@bsoubachov4https://github.com/go-openapi/testify/commits?author=bsoubachov
@uberbo4https://github.com/go-openapi/testify/commits?author=uberbo
@redachl3https://github.com/go-openapi/testify/commits?author=redachl
@spirin3https://github.com/go-openapi/testify/commits?author=spirin
@taichi3https://github.com/go-openapi/testify/commits?author=taichi
@zjx203https://github.com/go-openapi/testify/commits?author=zjx20
@senyast47453https://github.com/go-openapi/testify/commits?author=senyast4745
@andreas3https://github.com/go-openapi/testify/commits?author=andreas
@AngelKitty3https://github.com/go-openapi/testify/commits?author=AngelKitty
@hanzei3https://github.com/go-openapi/testify/commits?author=hanzei
@jonhoo3https://github.com/go-openapi/testify/commits?author=jonhoo
@reynieroz3https://github.com/go-openapi/testify/commits?author=reynieroz
@josephholsten3https://github.com/go-openapi/testify/commits?author=josephholsten
@nmiyake3https://github.com/go-openapi/testify/commits?author=nmiyake
@obeattie3https://github.com/go-openapi/testify/commits?author=obeattie
@ryu-ton102https://github.com/go-openapi/testify/commits?author=ryu-ton10
@snirye2https://github.com/go-openapi/testify/commits?author=snirye
@srenatus2https://github.com/go-openapi/testify/commits?author=srenatus
@xsleonard2https://github.com/go-openapi/testify/commits?author=xsleonard
@SuperQ2https://github.com/go-openapi/testify/commits?author=SuperQ
@viswajithiii2https://github.com/go-openapi/testify/commits?author=viswajithiii
@hawkingrei2https://github.com/go-openapi/testify/commits?author=hawkingrei
@yiminc-zz2https://github.com/go-openapi/testify/commits?author=yiminc-zz
@izaurio2https://github.com/go-openapi/testify/commits?author=izaurio
@gz-c2https://github.com/go-openapi/testify/commits?author=gz-c
@techfg2https://github.com/go-openapi/testify/commits?author=techfg
@visualfc2https://github.com/go-openapi/testify/commits?author=visualfc
@everdance2https://github.com/go-openapi/testify/commits?author=everdance
@greg0ire2https://github.com/go-openapi/testify/commits?author=greg0ire
@sikehish2https://github.com/go-openapi/testify/commits?author=sikehish
@LandonTClipp2https://github.com/go-openapi/testify/commits?author=LandonTClipp
@anupcshan2https://github.com/go-openapi/testify/commits?author=anupcshan
@AchoArnold2https://github.com/go-openapi/testify/commits?author=AchoArnold
@2pi2https://github.com/go-openapi/testify/commits?author=2pi
@bboreham2https://github.com/go-openapi/testify/commits?author=bboreham
@bmatsuo2https://github.com/go-openapi/testify/commits?author=bmatsuo
@dcormier2https://github.com/go-openapi/testify/commits?author=dcormier
@dencold2https://github.com/go-openapi/testify/commits?author=dencold
@siliconbrain2https://github.com/go-openapi/testify/commits?author=siliconbrain
@czeslavo2https://github.com/go-openapi/testify/commits?author=czeslavo
@ikravets2https://github.com/go-openapi/testify/commits?author=ikravets
@jeffwidman2https://github.com/go-openapi/testify/commits?author=jeffwidman
@joshrendek2https://github.com/go-openapi/testify/commits?author=joshrendek
@jcelliott2https://github.com/go-openapi/testify/commits?author=jcelliott
@mhamrle2https://github.com/go-openapi/testify/commits?author=mhamrle
@rinchsan2https://github.com/go-openapi/testify/commits?author=rinchsan
@mrbroll2https://github.com/go-openapi/testify/commits?author=mrbroll
@rubensayshi2https://github.com/go-openapi/testify/commits?author=rubensayshi
@renatoprime2https://github.com/go-openapi/testify/commits?author=renatoprime
@oalders2https://github.com/go-openapi/testify/commits?author=oalders
@rdwilliamson1https://github.com/go-openapi/testify/commits?author=rdwilliamson
@RmbRT1https://github.com/go-openapi/testify/commits?author=RmbRT
@RichardKnop1https://github.com/go-openapi/testify/commits?author=RichardKnop
@rgarcia1https://github.com/go-openapi/testify/commits?author=rgarcia
@pietern1https://github.com/go-openapi/testify/commits?author=pietern
@peterebden1https://github.com/go-openapi/testify/commits?author=peterebden
@pdbrito1https://github.com/go-openapi/testify/commits?author=pdbrito
@pquerna1https://github.com/go-openapi/testify/commits?author=pquerna
@pdufour1https://github.com/go-openapi/testify/commits?author=pdufour
@moolmanruan1https://github.com/go-openapi/testify/commits?author=moolmanruan
@rleungx1https://github.com/go-openapi/testify/commits?author=rleungx
@sam-nelson-mcn1https://github.com/go-openapi/testify/commits?author=sam-nelson-mcn
@g7r1https://github.com/go-openapi/testify/commits?author=g7r
@s-urbaniak1https://github.com/go-openapi/testify/commits?author=s-urbaniak
@shaneHowearth1https://github.com/go-openapi/testify/commits?author=shaneHowearth
@simonmulser1https://github.com/go-openapi/testify/commits?author=simonmulser
@brahmaroutu1https://github.com/go-openapi/testify/commits?author=brahmaroutu
@stdedos1https://github.com/go-openapi/testify/commits?author=stdedos
@aldas1https://github.com/go-openapi/testify/commits?author=aldas
@comogo1https://github.com/go-openapi/testify/commits?author=comogo
@TechnotronicOz1https://github.com/go-openapi/testify/commits?author=TechnotronicOz
@n9netales1https://github.com/go-openapi/testify/commits?author=n9netales
@anacrolix1https://github.com/go-openapi/testify/commits?author=anacrolix
@mmorel-351https://github.com/go-openapi/testify/commits?author=mmorel-35
@wallclockbuilder1https://github.com/go-openapi/testify/commits?author=wallclockbuilder
@MAnyKey1https://github.com/go-openapi/testify/commits?author=MAnyKey
@mchlp1https://github.com/go-openapi/testify/commits?author=mchlp
@lesichkovm1https://github.com/go-openapi/testify/commits?author=lesichkovm
@mlsteele1https://github.com/go-openapi/testify/commits?author=mlsteele
@neilconway1https://github.com/go-openapi/testify/commits?author=neilconway
@nbaztec1https://github.com/go-openapi/testify/commits?author=nbaztec
@bobuhiro111https://github.com/go-openapi/testify/commits?author=bobuhiro11
@doloopwhile1https://github.com/go-openapi/testify/commits?author=doloopwhile
@OladapoAjala1https://github.com/go-openapi/testify/commits?author=OladapoAjala
@omarkohl1https://github.com/go-openapi/testify/commits?author=omarkohl
@parkr1https://github.com/go-openapi/testify/commits?author=parkr
@pwfcurry1https://github.com/go-openapi/testify/commits?author=pwfcurry
@phemmer1https://github.com/go-openapi/testify/commits?author=phemmer
@stevenh1https://github.com/go-openapi/testify/commits?author=stevenh
@dave1https://github.com/go-openapi/testify/commits?author=dave
@GlenDC1https://github.com/go-openapi/testify/commits?author=GlenDC
@egawata1https://github.com/go-openapi/testify/commits?author=egawata
@hectorj1https://github.com/go-openapi/testify/commits?author=hectorj
@hidu1https://github.com/go-openapi/testify/commits?author=hidu
@lisitsky1https://github.com/go-openapi/testify/commits?author=lisitsky
@lummie1https://github.com/go-openapi/testify/commits?author=lummie
@myxo1https://github.com/go-openapi/testify/commits?author=myxo
@nicoche1https://github.com/go-openapi/testify/commits?author=nicoche
@perrydunn1https://github.com/go-openapi/testify/commits?author=perrydunn
@philtay1https://github.com/go-openapi/testify/commits?author=philtay
@renzoarreaza1https://github.com/go-openapi/testify/commits?author=renzoarreaza
@sarathsp061https://github.com/go-openapi/testify/commits?author=sarathsp06
@sunpe1https://github.com/go-openapi/testify/commits?author=sunpe
@timfeirg1https://github.com/go-openapi/testify/commits?author=timfeirg
@tscales1https://github.com/go-openapi/testify/commits?author=tscales
@api-card1https://github.com/go-openapi/testify/commits?author=api-card
@wwade1https://github.com/go-openapi/testify/commits?author=wwade
@agonzalezro1https://github.com/go-openapi/testify/commits?author=agonzalezro
@hikyaru-suzuki1https://github.com/go-openapi/testify/commits?author=hikyaru-suzuki
@groz1https://github.com/go-openapi/testify/commits?author=groz
@tedeh1https://github.com/go-openapi/testify/commits?author=tedeh
@terinjokes1https://github.com/go-openapi/testify/commits?author=terinjokes
@guettli1https://github.com/go-openapi/testify/commits?author=guettli
@tschaub1https://github.com/go-openapi/testify/commits?author=tschaub
@tobikris1https://github.com/go-openapi/testify/commits?author=tobikris
@TomWright1https://github.com/go-openapi/testify/commits?author=TomWright
@prochac1https://github.com/go-openapi/testify/commits?author=prochac
@tabboud1https://github.com/go-openapi/testify/commits?author=tabboud
@xtonyjiang1https://github.com/go-openapi/testify/commits?author=xtonyjiang
@tcsc1https://github.com/go-openapi/testify/commits?author=tcsc
@qerdcv1https://github.com/go-openapi/testify/commits?author=qerdcv
@vincentcr1https://github.com/go-openapi/testify/commits?author=vincentcr
@vitalyisaev21https://github.com/go-openapi/testify/commits?author=vitalyisaev2
@marshall-lee1https://github.com/go-openapi/testify/commits?author=marshall-lee
@willogden1https://github.com/go-openapi/testify/commits?author=willogden
@ybrustin1https://github.com/go-openapi/testify/commits?author=ybrustin
@yarikk1https://github.com/go-openapi/testify/commits?author=yarikk
@ac71https://github.com/go-openapi/testify/commits?author=ac7
@cuishuang1https://github.com/go-openapi/testify/commits?author=cuishuang
@bogdandrutu1https://github.com/go-openapi/testify/commits?author=bogdandrutu
@docbobo1https://github.com/go-openapi/testify/commits?author=docbobo
@moorereason1https://github.com/go-openapi/testify/commits?author=moorereason
@chakrit1https://github.com/go-openapi/testify/commits?author=chakrit
@lazywei1https://github.com/go-openapi/testify/commits?author=lazywei
@hugelgupf1https://github.com/go-openapi/testify/commits?author=hugelgupf
@csmarchbanks1https://github.com/go-openapi/testify/commits?author=csmarchbanks
@cam72cam1https://github.com/go-openapi/testify/commits?author=cam72cam
@connor43121https://github.com/go-openapi/testify/commits?author=connor4312
@shousper1https://github.com/go-openapi/testify/commits?author=shousper
@coryb1https://github.com/go-openapi/testify/commits?author=coryb
@SchumacherFM1https://github.com/go-openapi/testify/commits?author=SchumacherFM
@danielheller1https://github.com/go-openapi/testify/commits?author=danielheller
@danielchatfield1https://github.com/go-openapi/testify/commits?author=danielchatfield
@danielwhite1https://github.com/go-openapi/testify/commits?author=danielwhite
@hairyhenderson1https://github.com/go-openapi/testify/commits?author=hairyhenderson
@davidjb1https://github.com/go-openapi/testify/commits?author=davidjb
@dpw1https://github.com/go-openapi/testify/commits?author=dpw
@dennwc1https://github.com/go-openapi/testify/commits?author=dennwc
@lowjoel1https://github.com/go-openapi/testify/commits?author=lowjoel
@3scalation1https://github.com/go-openapi/testify/commits?author=3scalation
@AutomateAaron1https://github.com/go-openapi/testify/commits?author=AutomateAaron
@adamluzsi1https://github.com/go-openapi/testify/commits?author=adamluzsi
@adamwg1https://github.com/go-openapi/testify/commits?author=adamwg
@packrat3861https://github.com/go-openapi/testify/commits?author=packrat386
@atombender1https://github.com/go-openapi/testify/commits?author=atombender
@AlekSi1https://github.com/go-openapi/testify/commits?author=AlekSi
@kejadlen1https://github.com/go-openapi/testify/commits?author=kejadlen
@alxn1https://github.com/go-openapi/testify/commits?author=alxn
@aud10slave1https://github.com/go-openapi/testify/commits?author=aud10slave
@ErebusBat1https://github.com/go-openapi/testify/commits?author=ErebusBat
@shazow1https://github.com/go-openapi/testify/commits?author=shazow
@tamccall1https://github.com/go-openapi/testify/commits?author=tamccall
@Ararsa-Derese1https://github.com/go-openapi/testify/commits?author=Ararsa-Derese
@bozaro1https://github.com/go-openapi/testify/commits?author=bozaro
@bartventer1https://github.com/go-openapi/testify/commits?author=bartventer
@bwplotka1https://github.com/go-openapi/testify/commits?author=bwplotka
@bayesianmind1https://github.com/go-openapi/testify/commits?author=bayesianmind
@benbjohnson1https://github.com/go-openapi/testify/commits?author=benbjohnson
@blakewilson-wf1https://github.com/go-openapi/testify/commits?author=blakewilson-wf
@jonasfj1https://github.com/go-openapi/testify/commits?author=jonasfj
@jcamenisch1https://github.com/go-openapi/testify/commits?author=jcamenisch
@JonCrowther1https://github.com/go-openapi/testify/commits?author=JonCrowther
@ernsheong1https://github.com/go-openapi/testify/commits?author=ernsheong
@jinnovation1https://github.com/go-openapi/testify/commits?author=jinnovation
@xordspar01https://github.com/go-openapi/testify/commits?author=xordspar0
@jayd3e1https://github.com/go-openapi/testify/commits?author=jayd3e
@capoferro1https://github.com/go-openapi/testify/commits?author=capoferro
@nyarly1https://github.com/go-openapi/testify/commits?author=nyarly
@justbuchanan1https://github.com/go-openapi/testify/commits?author=justbuchanan
@jedevc1https://github.com/go-openapi/testify/commits?author=jedevc
@ingwarsw1https://github.com/go-openapi/testify/commits?author=ingwarsw
@navytux1https://github.com/go-openapi/testify/commits?author=navytux
@itzamna3141https://github.com/go-openapi/testify/commits?author=itzamna314
@leepa1https://github.com/go-openapi/testify/commits?author=leepa
@luan1https://github.com/go-openapi/testify/commits?author=luan
@Lucaber1https://github.com/go-openapi/testify/commits?author=Lucaber
@miparnisari1https://github.com/go-openapi/testify/commits?author=miparnisari
@martin-sucha1https://github.com/go-openapi/testify/commits?author=martin-sucha
@dominicbarnes1https://github.com/go-openapi/testify/commits?author=dominicbarnes
@dlclark1https://github.com/go-openapi/testify/commits?author=dlclark
@bits011https://github.com/go-openapi/testify/commits?author=bits01
@dmacvicar1https://github.com/go-openapi/testify/commits?author=dmacvicar
@echarrod1https://github.com/go-openapi/testify/commits?author=echarrod
@evanphx1https://github.com/go-openapi/testify/commits?author=evanphx
@fahimbagar1https://github.com/go-openapi/testify/commits?author=fahimbagar
@gavincabbage1https://github.com/go-openapi/testify/commits?author=gavincabbage
@glesica1https://github.com/go-openapi/testify/commits?author=glesica
@guncha1https://github.com/go-openapi/testify/commits?author=guncha
@cryptix1https://github.com/go-openapi/testify/commits?author=cryptix
@henrahmagix1https://github.com/go-openapi/testify/commits?author=henrahmagix
@hslatman1https://github.com/go-openapi/testify/commits?author=hslatman
@chirino1https://github.com/go-openapi/testify/commits?author=chirino
@ianrose141https://github.com/go-openapi/testify/commits?author=ianrose14
@ifraixedes1https://github.com/go-openapi/testify/commits?author=ifraixedes
@jszwec1https://github.com/go-openapi/testify/commits?author=jszwec
@programmer041https://github.com/go-openapi/testify/commits?author=programmer04
@jaguilar1https://github.com/go-openapi/testify/commits?author=jaguilar
@jbowes1https://github.com/go-openapi/testify/commits?author=jbowes

this file was generated by the Contributors GitHub Action

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Code Of Conduct

Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

Our Standards

Examples of behavior that contributes to creating a positive environment include:

  • Using welcoming and inclusive language
  • Being respectful of differing viewpoints and experiences
  • Gracefully accepting constructive criticism
  • Focusing on what is best for the community
  • Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

  • The use of sexualized language or imagery and unwelcome sexual attention or advances
  • Trolling, insulting/derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others’ private information, such as a physical or electronic address, without explicit permission
  • Other conduct which could reasonably be considered inappropriate in a professional setting

Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ivan+abuse@flanders.co.nz. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://contributor-covenant.org/version/1/4

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Security Policy

This policy outlines the commitment and practices of the go-openapi maintainers regarding security.

Supported Versions

VersionSupported
2.2.x

Vulnerability checks in place

This repository uses automated vulnerability scans, at every merged commit and at least once a week.

We use:

Reports are centralized in github security reports and visible only to the maintainers.

Reporting a vulnerability

If you become aware of a security vulnerability that affects the current repository, please report it privately to the maintainers rather than opening a publicly visible GitHub issue.

Please follow the instructions provided by github to Privately report a security vulnerability.

TL;DR

On Github, navigate to the project’s “Security” tab then click on “Report a vulnerability”.

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Maintainer's Guidelines

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Subsections of Maintainer's Guidelines

Maintainer's Guide

TL;DR

Comprehensive guide for maintainers covering repository structure, CI/CD workflows, release procedures, and development practices. Essential reading for anyone contributing to or maintaining this project.

Repo structure

This project is organized as a monorepo with multiple go modules.

Repo configuration

  • Default branch: master
  • Protected branches: master
  • Branch protection rules:
    • require pull requests and approval
    • required status checks:
      • DCO (simple email sign-off)
      • Lint
      • All tests completed
  • Auto-merge enabled (used for dependabot updates and other auto-merged PR’s, e.g. contributors update)

Continuous Integration

Code Quality checks

NOTES

codefactor inherits roles from github. There is no need to create a dedicated account.

The codefactor app is installed at the organization level (github.com/go-openapi).

There is no special token to setup in github for CI usage.

Testing

  • Test reports

  • Test coverage reports

  • Fuzz testing

    • Fuzz tests are handled separately by CI and may reuse a cached version of the fuzzing corpus. At this moment, cache may not be shared between feature branches or feature branch and master. The minimized corpus produced on failure is uploaded as an artifact and should be added manually to testdata/fuzz/....

Coverage threshold status is informative and not blocking. This is because the thresholds are difficult to tune and codecov oftentimes reports false negatives or may fail to upload coverage.

All tests across go-openapi use our fork of stretchr/testify (this repo): github.com/go-openapi/testify. This allows for minimal test dependencies.

NOTES

codecov inherits roles from github. There is no need to create a dedicated account. However, there is only 1 maintainer allowed to be the admin of the organization on codecov with their free plan.

The codecov app is installed at the organization level (github.com/go-openapi).

There is no special token to setup in github for CI usage. A organization-level token used to upload coverage and test reports is managed at codecov: no setup is required on github.

Automated updates

  • dependabot

    Principle:

    • codecov applies updates and security patches to the github-actions and golang ecosystems.
    • all updates from “trusted” dependencies (github actions, golang.org packages, go-openapi packages are auto-merged if they successfully pass CI.
  • go version udpates

    Principle:

    • we support the 2 latest minor versions of the go compiler (stable, oldstable)
    • go.mod should be updated (manually) whenever there is a new go minor release (e.g. every 6 months).

    This means that our projects always have a 6 months lag to enforce new features from the go compiler.

    However, new features of go may be used with a “go:build” tag: this allows users of the newer version to benefit the new feature while users still running with oldstable use another version that still builds.

  • contributors

    • a CONTRIBUTORS.md file is updated weekly, with all-time contributors to the repository
    • the github-actions[bot] posts a pull request to do that automatically
    • at this moment, this pull request is not auto-approved/auto-merged (bot cannot approve its own PRs)

Vulnerability scanners

There are 3 complementary scanners - obviously, there is some overlap, but each has a different focus.

None of these tools require an additional account or token.

Github CodeQL configuration is set to “Advanced”, so we may collect a CI status for this check (e.g. for badges).

Scanners run on every commit to master and at least once a week.

Reports are centralized in github security reports for code scanning tools.

Releases

For single module repos:

A bump release workflow can be triggered from the github actions UI to cut a release with a few clicks.

The release process is minimalist:

  • push a semver tag (i.e v{major}.{minor}.{patch}) to the master branch.

  • the CI handles this to generate a github release with release notes

  • release notes generator: git-cliff https://git-cliff.org/docs/

  • configuration: the .cliff.toml is defined as a share configuration on remote repo ci-worflows/.cliff.toml

Commits from maintainers are preferably PGP-signed.

Tags are preferably PGP-signed.

We want our released to show as “verified” on github.

The tag message introduces the release notes (e.g. a summary of this release).

The release notes generator does not assume that commits are necessarily “conventional commits”.

For mono-repos with multiple modules:

The release process is slightly different because we need to update cross-module dependencies before pushing a tag.

A bump release workflow (mono-repo) can be triggered from the github actions UI to cut a release with a few clicks.

It works with the same input as the one for single module repos, and first creates a PR (auto-merged) that updates the different go.mod files before pushing the desired git tag.

Commits and tags pushed by the workflow bot are PGP-signed (“go-openapi[bot]”).

Other files

Standard documentation:

Reference documentation (released):

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Architecture

Primer

Goals

We want the maintainance of dozens of test assertions, times many variants, to remain reasonably low.

The maintainance flow is intended to require different activities and levels of understanding, dependending on the complexity of a planned evolution.

journey
    section Fixes & minor enhancements
      internal/assertions:5: Knowledge of the functionality
    section New dependencies
      internal/assertions/enable/...:5: Understanding of the repo architecture
      enable/...:5:  Understanding of the repo architecture
    section API changes
      regenerate code:5: No specific knowledge
    section New constructs to support
      code & doc generator:5: Knowledge of internals

Most common maintenance tasks should not require much more than fixing/enhancing the code in internal/assertions.

API changes need an extra code generation.

Dependency changes (adding new features that need extra dependencies) is a bit more involved, but still manageable.

The code & doc generator should rapidly become a very stable component. The maintenance of the generator itself remains an operation that requires an extended understanding of the internals of the project.

Fixes and enhancements propagate naturally to the variants without the need to regenerate code.

The maths with assertion variants

Each test assertion produces 2 base variants (assert, require).

Each of these variants produces another formatted variant. Except for generic assertions, we produce one “forward” variant and one “forward formatted” variant (as methods).

For every non-generic assertion: 8 variants.

For every generic assertion: 4 variants.

For every “helper” function (not an assertion): 2 variants.

All these variants make up several hundreds functions, which poses a challenge for maintainance and documentation.

We have adopted code and documentation generation as a mean to mitigate this issue.

Current (v2.3.0-unreleased)

  1. Generic assertions (with type parameters): 42 functions
  2. Non-generic assertions (with t T parameter, no type parameters): 82 functions
  3. Helper functions (no t T parameter): 4 functions

Total: 128 functions to maintain

Generated Functions

  1. Generic assertions: 168
  2. Non-generic assertions: 656
  3. Helper functions: 8
  4. Constructors: 2

Total: 834 functions

Architecture Overview

graph LR;
  classDef event font-size:small,font-family:Monospace;
  trigger@{ shape: rounded, label: "go generate" }
  codegen[["🛠️ codegen"]]
  docs@{ shape: documents, label: "📚 API docs"}
  source[["📦 internal/assertions
    (source of truth)"]]

  trigger:::event -.-> codegen
  source --> codegen --> assert
  source --> codegen --> require
  codegen --> docs

Single Source of Truth: internal/assertions/

All assertion implementations live in internal/assertions/, organized by domain:

  • Functions are implemented once with comprehensive tests
  • Doc comments include “Examples:” sections that drive test generation (including testable examples)
  • Both assert/ and require/ packages are 100% generated from this source

Code Generator: codegen/

The generator scans internal/assertions/ and produces:

  • Package-level functions (assert.Equal, require.Equal)
  • Format variants (assert.Equalf, require.Equalf)
  • Forward methods (a.Equal(), r.Equal())
  • Tests for all variants
  • Testable examples for godoc
  • Documentation for documentation site, organized by domain

Generated Packages: assert/ and require/

The generated functions directly call the internal implementation: no code duplication or change in semantics.

Generated Documentation: docs/doc-site/api/

Everything in these packages is generated. Never edit generated files directly.

Exceptions:

  • doc.go is not generated
  • ad’hoc testable examples are not generated

Optional Feature Packages: enable/

The enable/ package provides optional features that users can activate via blank imports:

  • enable/stubs/ - Public stub APIs for enabling features (yaml, colors)
  • enable/yaml/ - Activates YAML support via import _ "github.com/go-openapi/testify/v2/enable/yaml"
  • enable/colors/ - Activates colorized output via import _ "github.com/go-openapi/testify/v2/enable/colors"

These packages are not generated and allow optional dependencies to be isolated from the core library.

See Also

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Code Generation

TL;DR

The entire assert/require API (600+ functions) is generated from 76 source assertions in internal/assertions/. Run go generate ./... to regenerate everything. Add new assertions by editing source files and adding examples.

Maintaining Generated Code

This repository uses code generation extensively to maintain consistency across assertion packages.

Code Generation Pipeline

  graph TD
    source["📦 internal/assertions/*.go"]
    scanner["🔍 Scanner
    go/packages + go/types"]
    model["fa:fa-database
    Model data structures"]
    templates["📝 Templates
    Go text/template"]
    outputs["📤 Generated Code"]

    source --> scanner
    scanner --> extract_metadata
    extract_metadata --> model
    model --> templates
    templates --> outputs

    subgraph extract_metadata["Extract Metadata"]
      direction BT
      extract["Extractor"]
      comments["godoc comments"] --o extract
      examples["examples: values comments"] --o extract
      domains["domain tags"] --o extract
      sigs["Function signatures"] --o extract
      sigs["Other internal annotations comments"] --o extract
    end

    outputs -.-> assert_package
    outputs -.-> require_package
    outputs -.-> docs@{shape: documents, label: "docs/doc-site/**/*.md"}
    
    subgraph assert_package
      direction BT
      assert@{shape: documents, label: "assert/*.go"}
      tests_assert["*_test.go files"] --o assert
      example_tests_assert["*_examples_test.go files"] --o assert

      subgraph not_generated_assert["*not generated*"]
        direction LR
        docgo_assert@{ shape: document, label: "doc.go" }
        adhoc_assert@{ shape: document, label: "*_adhoc*_test.go" }
      end
    end

    subgraph require_package
      direction BT

      require@{shape: documents, label: "require/*.go"}
      tests_require["*_test.go files"] --o require
      example_tests_require["*_examples_test.go files"] --o require

      subgraph not_generated_require["*not generated*"]
        direction LR
        docgo_require@{ shape: document, label: "doc.go" }
        adhoc_require@{ shape: document, label: "*_adhoc*_test.go" }
      end
    end

    style not_generated_assert fill:#4a9eff,color:#fff
    style not_generated_require fill:#4a9eff,color:#fff

The generator scans source code, extracts metadata, builds a model, and applies templates to generate ~800+ functions, tests, and documentation from ~100+ source functions.


Adding a New Assertion

Complete workflow:

  1. Add function to internal/assertions/<domain>.go:

    The following example would like go to string.go, next to the Regexp assertion.

    import (
    	"fmt"
    	"strings"
    )
    
    // StartsWith asserts that the string starts with the given prefix.
    //
    // Examples:
    //
    //	success: "hello world", "hello"
    //	failure: "hello world", "bye"
    func StartsWith(t T, str, prefix string, msgAndArgs ...any) bool {
    	if h, ok := t.(H); ok {
    		h.Helper()
    	}
    	if !strings.HasPrefix(str, prefix) {
    		return Fail(t, fmt.Sprintf("Expected %q to start with %q", str, prefix), msgAndArgs...)
    	}
    	return true
    }
  2. Add tests to internal/assertions/<domain>_test.go: Write comprehensive table-driven tests covering edge cases.

  3. Run code generation:

    go generate ./...
  4. Done! All 8 variants are generated with tests and examples:

    • assert.StartsWith(t, str, prefix)
    • assert.StartsWithf(t, str, prefix, "msg")
    • a.StartsWith(str, prefix) (forward method)
    • a.StartsWithf(str, prefix, "msg")
    • require.StartsWith(t, str, prefix)
    • require.StartsWithf(t, str, prefix, "msg")
    • r.StartsWith(str, prefix) (forward method)
    • r.StartsWithf(str, prefix, "msg")

How One Function Becomes Eight

graph TD
    source["1 Source Function
    internal/assertions/Equal()"]

    source --> assert_group["assert Package"]
    source --> require_group["require Package"]

    assert_group --> assert_pkg["assert.Equal(t, a, b)
    package-level"]
    assert_group --> assert_fmt["assert.Equalf(t, a, b, msg)
    formatted variant"]
    assert_group --> assert_fwd["a.Equal(a, b)
    forward method"]
    assert_group --> assert_fwdfmt["a.Equalf(a, b, msg)
    forward + formatted"]

    require_group --> require_pkg["require.Equal(t, a, b)
    package-level (fatal)"]
    require_group --> require_fmt["require.Equalf(t, a, b, msg)
    formatted variant (fatal)"]
    require_group --> require_fwd["r.Equal(a, b)
    forward method (fatal)"]
    require_group --> require_fwdfmt["r.Equalf(a, b, msg)
    forward + formatted (fatal)"]

    style source fill:#4a9eff,color:#fff
    style assert_group fill:#90ee90,color:#000
    style require_group fill:#ffb6c1,color:#000

reflection-based assertions become 8, generic assertions become 4

(plus tests and documentation for each).


Example Annotations Format

The “Examples:” section in doc comments drives test and example generation:

// Examples:
//
//   success: <test arguments that should succeed>
//   failure: <test arguments that should fail>
//   panic: <test arguments that cause panic>
//          <expected panic message>

Rules:

  • Use valid Go expressions that can be directly inserted into test code
  • success: and failure: are required for most assertions
  • panic: is optional (used for assertions like Panics, YAMLEq)
  • Multiple examples of the same type are allowed (e.g., multiple success: lines)
  • Examples are extracted by the scanner and used to generate:
    • Unit tests (success + failure cases)
    • Testable examples (success cases only for simplicity)

Example with multiple success cases:

// Examples:
//
//   success: []string{"a", "b"}, 2        // slice
//   success: map[string]int{"a": 1}, 1    // map
//   success: "hello", 5                    // string
//   failure: []string{"a"}, 5

Example-Driven Test Generation

  graph LR
    doccomment["Doc Comment
    with Examples:"]
    parser["📖 Example Parser"]
    cases["Test Cases
    success/failure/panic"]
    multiplier["Multiply × 8"]
    tests["Generated Tests"]

    doccomment --> parser
    parser --> cases
    cases --> multiplier

    multiplier --> pkg_assert["assert package test"]
    multiplier --> fmt_assert["assert format test"]
    multiplier --> fwd_assert["assert forward test"]
    multiplier --> fwdfmt_assert["assert fwd+fmt test"]
    multiplier --> pkg_require["require package test"]
    multiplier --> fmt_require["require format test"]
    multiplier --> fwd_require["require forward test"]
    multiplier --> fwdfmt_require["require fwd+fmt test"]

    pkg_assert & fmt_assert & fwd_assert & fwdfmt_assert & pkg_require & fmt_require & fwd_require & fwdfmt_require --> tests

    style cases fill:orange,color:black;
    style multiplier fill:yellow,color:black;
    style tests fill:lightgreen,color:black;

Each example in doc comments generates 8 test functions (one per variant), ensuring 100% test coverage of generated code. In addition, the generator produces testable examples (somewhat redundant with “passed” tests) so every function gets a testable example displayed on pkg.go.dev.


Special Cases in Generated Tests

For complex assertions requiring special setup, the test templates support conditional logic. See codegen/internal/generator/templates/assertion_assertions_test.gotmpl for examples of:

  • Custom mock selection based on function behavior (mockT vs mockFailNowT)
  • Package-specific test helpers (testDataPath, httpOK, etc.)
  • Handling functions without test examples (generates t.Skip())

Some go expressions won’t fit nicely for examples (examples use an external package, e.g. assert_test). To cover these edge cases, a relocate function map currently rewrites the example values to be used from an external package. The relocation uses go parsing capabilities. The only hard-coded exception if for PanicFunc. (see codegen/internal/generator/funcmap.go).

Generator Flags

go run ./codegen/main.go \
	-work-dir=.. \
	-input-package=github.com/go-openapi/testify/v2/internal/assertions \
	-output-packages=assert,require \
	-target-root=.. \
	-include-format-funcs=true \
	-include-forward-funcs=true \
	-include-tests=true \
	-include-examples=true \
	-runnable-examples=true \
	-include-helpers=true \
	-include-generics=false

Current usage with go generate (see doc.go):

//go:generate go run ./codegen/main.go -target-root . -work-dir .

Note: Generic functions are planned but not yet implemented.

Verification

After generation, verify:

# All tests pass
go test ./...

# Coverage remains high
go test -cover ./internal/assertions # Should be ~94%
go test -cover ./assert              # Should be ~99.5%
go test -cover ./require             # Should be ~99.5%

# Examples are valid
go test -run Example ./assert
go test -run Example ./require

The 0.5% coverage gap comes from helper functions (non-assertion functions) that don’t have “Examples:” annotations.

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Optional Dependencies

TL;DR

The main module has zero external dependencies. Optional features (YAML, colorized output) are activated by importing separate enable/ modules. Internal stubs panic with helpful messages when a feature is used without being enabled.

The Problem

Testing libraries sit at the bottom of the dependency tree: every package in a project imports them. Any dependency pulled in by the testing library propagates to all consumers. The original stretchr/testify pulls in gopkg.in/yaml.v3, github.com/davecgh/go-spew, and github.com/pmw/go-difflib for all users, even those who never call YAMLEq.

Our goal: zero external dependencies in the main module, with opt-in features for users who need them.

Architecture Overview

Three layers collaborate to deliver optional features without coupling:

graph TD
    user["User Code"]
    enablemod["enable/yaml  or  enable/colors<br/><i>separate Go modules</i>"]
    stubs["enable/stubs/yaml  or  enable/stubs/colors<br/><i>public delegation API</i>"]
    internal["internal/assertions/enable/yaml  or  .../colors<br/><i>internal stubs with function pointers</i>"]
    assertions["internal/assertions/yaml.go  or  equal.go, diff.go<br/><i>assertion implementations</i>"]

    user -- "blank import" --> enablemod
    enablemod -- "init(): wires real impl" --> stubs
    stubs -- "delegates to" --> internal
    assertions -- "calls" --> internal

    style enablemod fill:#4a9eff,color:#fff
    style stubs fill:#90ee90,color:#000
    style internal fill:#ffb6c1,color:#000
    style assertions fill:#f0f0f0,color:#000
LayerLocationHas external deps?Purpose
Feature moduleenable/yaml/, enable/colors/Yes (own go.mod)Imports the real library, wires it in via init()
Public stubsenable/stubs/yaml/, enable/stubs/colors/NoStable public API that delegates to internal package
Internal stubsinternal/assertions/enable/yaml/, .../colors/NoHolds function pointers, panics when unset
Assertionsinternal/assertions/*.goNoCalls internal stubs; unaware of external libraries

How It Works: YAML

The Wiring Chain

User imports:  _ "github.com/go-openapi/testify/enable/yaml/v2"
                   │
                   ▼
enable/yaml/enable_yaml.go  init()
  ├─ calls yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal)
  └─ calls yamlstub.EnableYAMLWithMarshal(yaml.Marshal)
                   │
                   ▼
enable/stubs/yaml/enable_yaml.go
  └─ delegates to internal/assertions/enable/yaml
                   │
                   ▼
internal/assertions/enable/yaml/enable_yaml.go
  └─ stores function pointers in package-level vars
                   │
                   ▼
internal/assertions/yaml.go
  └─ calls yaml.Unmarshal() / yaml.Marshal() via the stored pointers

What Happens Without Enablement

If a user calls assert.YAMLEq(t, a, b) without the blank import, the internal stub panics:

panic: YAML is not enabled. To enable YAML support, add a blank import:

  import _ "github.com/go-openapi/testify/enable/yaml/v2"

This is intentional: fail fast with a clear fix, rather than silently returning wrong results.

Internal Stub Pattern (YAML)

The internal stub stores function pointers that start as nil:

// internal/assertions/enable/yaml/enable_yaml.go

var (
    enableYAMLUnmarshal func([]byte, any) error
    enableYAMLMarshal   func(any) ([]byte, error)
)

func Unmarshal(in []byte, out any) error {
    if enableYAMLUnmarshal == nil {
        panic("YAML is not enabled...")
    }
    return enableYAMLUnmarshal(in, out)
}

Feature Module (YAML)

The feature module is a separate Go module with its own go.mod:

// enable/yaml/go.mod
module github.com/go-openapi/testify/enable/yaml/v2

require go.yaml.in/yaml/v3 v3.0.4

Its init() wires in the real implementation:

// enable/yaml/enable_yaml.go

func init() {
    yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal)
    yamlstub.EnableYAMLWithMarshal(yaml.Marshal)
}

Custom YAML Library

Users can bypass the default enable/yaml module and inject their own YAML library:

import (
    yaml "github.com/goccy/go-yaml"
    yamlstub "github.com/go-openapi/testify/v2/enable/stubs/yaml"
)

func init() {
    yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal)
}

This works because the public stubs API accepts any function matching the expected signature.


How It Works: Colorized Output

The Wiring Chain

User imports:  _ "github.com/go-openapi/testify/enable/colors/v2"
                   │
                   ▼
enable/colors/enable.go  init()
  ├─ registers CLI flags: -testify.colorized, -testify.theme, -testify.colorized.notty
  ├─ reads env vars: TESTIFY_COLORIZED, TESTIFY_THEME, TESTIFY_COLORIZED_NOTTY
  ├─ detects TTY via golang.org/x/term
  └─ calls colorstub.Enable(func() []Option { ... })
                   │
                   ▼
enable/stubs/colors/enable_colors.go
  └─ delegates Enable() to internal/assertions/enable/colors
                   │
                   ▼
internal/assertions/enable/colors/enable_colors.go
  └─ stores enabler function, resolves lazily via sync.Once
                   │
                   ▼
internal/assertions/equal.go, diff.go
  └─ calls colors.ExpectedColorizer(), colors.ActualColorizer(), colors.Options()

Lazy Initialization

Colors use a different pattern than YAML: lazy initialization with sync.Once.

// internal/assertions/enable/colors/enable_colors.go

var (
    resolveOptionsOnce sync.Once
    optionsEnabler     func() []Option
)

func Enable(enabler func() []Option) {
    optionsEnabler = enabler
}

func resolveOptions() {
    resolveOptionsOnce.Do(func() {
        if optionsEnabler == nil {
            // Not enabled: use no-op colorizers
            return
        }
        // Enabled: build ANSI colorizers from options
        o := optionsWithDefaults(optionsEnabler())
        colorOptions = makeDiffOptions(o)
        stringColorizers = setColorizers(o)
    })
}

Why lazy? The init() function in enable/colors registers CLI flags, but flag values are only available after flag.Parse() runs (which happens when the test binary starts). The sync.Once defers resolution until the first assertion call, when flags are ready.

Why not panic? Colors are purely cosmetic. If not enabled, assertions work identically with plain text output. No-op colorizers are used by default.

Feature Module (Colors)

// enable/colors/go.mod
module github.com/go-openapi/testify/enable/colors/v2

require golang.org/x/term v0.39.0

The golang.org/x/term dependency is used solely for TTY detection (term.IsTerminal).


Design Decisions

Why Three Layers?

A simpler two-layer design (feature module calls internal directly) would work, but three layers provide:

  1. Stable public APIenable/stubs/ is the contract users depend on for custom wiring. Internal package paths can change without breaking user code.
  2. Testability – stubs can be tested independently of feature modules.
  3. Substitutability – users can wire alternative implementations through the stubs API without importing the default feature module.

Panic vs. Silent Degradation

FeatureStrategyReason
YAMLPanicIncorrect results are worse than a crash. If YAMLEq silently fails, tests pass when they shouldn’t.
ColorsNo-opMissing colors don’t affect correctness. Assertions work fine without ANSI codes.

Why Separate Go Modules?

Each enable/ feature is its own Go module (own go.mod). This means:

  • go mod tidy in the main module never pulls in go.yaml.in/yaml/v3 or golang.org/x/term
  • Users who go get github.com/go-openapi/testify/v2 get zero transitive dependencies
  • Feature dependencies are resolved only when the feature module is imported

Adding a New Optional Feature

Follow these steps to add a new opt-in feature (e.g., a hypothetical JSON schema validator):

1. Create internal stubs

// internal/assertions/enable/jsonschema/enable.go
package jsonschema

var validateFunc func(schema, document []byte) error

func Validate(schema, document []byte) error {
    if validateFunc == nil {
        panic(`JSON Schema validation is not enabled. Import:
  _ "github.com/go-openapi/testify/enable/jsonschema/v2"`)
    }
    return validateFunc(schema, document)
}

func EnableWithValidate(fn func([]byte, []byte) error) {
    validateFunc = fn
}

2. Create public stubs

// enable/stubs/jsonschema/enable.go
package jsonschema

import internal "github.com/go-openapi/testify/v2/internal/assertions/enable/jsonschema"

func EnableWithValidate(fn func([]byte, []byte) error) {
    internal.EnableWithValidate(fn)
}

3. Create feature module

// enable/jsonschema/go.mod
module github.com/go-openapi/testify/enable/jsonschema/v2

require github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
// enable/jsonschema/enable.go
package jsonschema

import (
    "github.com/santhosh-tekuri/jsonschema/v6"
    stub "github.com/go-openapi/testify/v2/enable/stubs/jsonschema"
)

func init() {
    stub.EnableWithValidate(func(schema, doc []byte) error {
        // wire in the real validator
    })
}

4. Use in assertions

// internal/assertions/jsonschema.go
package assertions

import jsonschema "github.com/go-openapi/testify/v2/internal/assertions/enable/jsonschema"

func JSONSchemaValid(t T, schema, document []byte, msgAndArgs ...any) bool {
    if h, ok := t.(H); ok {
        h.Helper()
    }
    if err := jsonschema.Validate(schema, document); err != nil {
        return Fail(t, fmt.Sprintf("JSON Schema validation failed: %s", err), msgAndArgs...)
    }
    return true
}

5. Add integration tests

Create internal/testintegration/jsonschema/ with tests that import the feature module and exercise the assertion end-to-end. Update internal/testintegration/go.mod to add the new dependency.


Integration Testing

Optional features require a separate Go module for end-to-end testing, since the main module cannot import external dependencies.

The internal/testintegration/ module serves this purpose:

internal/testintegration/
├── go.mod           # Imports: yaml, colors, rapid
├── yaml/            # Tests YAMLEq with real YAML parser
├── colors/          # Tests colorized output with ANSI detection
└── spew/            # Property-based testing (unrelated to opt-in features)

See internal/testintegration/README.md for details on running these tests.


Module Map

graph LR
    main["github.com/go-openapi/testify/v2<br/><b>main module</b><br/><i>zero dependencies</i>"]
    yaml_mod["enable/yaml/v2<br/><i>go.yaml.in/yaml/v3</i>"]
    colors_mod["enable/colors/v2<br/><i>golang.org/x/term</i>"]
    testint["internal/testintegration/v2<br/><i>yaml + colors + rapid</i>"]

    yaml_mod -- "replace =>" --> main
    colors_mod -- "replace =>" --> main
    testint -- "replace =>" --> main

    style main fill:#4a9eff,color:#fff
    style yaml_mod fill:#90ee90,color:#000
    style colors_mod fill:#90ee90,color:#000
    style testint fill:#ffb6c1,color:#000

All feature modules use replace directives to point to the local main module during development. In production, Go module resolution handles versioning automatically.

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Roadmap

What’s next with this project?

timeline
    title Planned releases
    section Q4 2025
    ✅ v2.0 (Nov 2025) : Zero dependencies
                    : optional dependencies (YAML)
                    : modernized code (relint)
                    : JSONEqBytes
    section Q1 2026
    ✅ v2.1 (Jan 2026) : Generated assertions
                    : complete refactoring
                    : documentation site
                    : panic handling fixes
                    : removed deprecated
    ✅ v2.2 (Fev 2026) : Generics
                    : Kind/NotKind
                    : SortedT, NotSortedT
                    : complete test refactoring
                    : more benchmarks. Perf improvements
                    : optional dependencies (colorized)
    ✅ v2.3 (Fev 2026) : Other extensions
                    : Extensible Assertion type
                    : JSON & YAML assertions: JSONMarshalsAs...
                    : NoGoroutineLeak
                    : more documentation and examples
    ⏳v2.4 (Mar 2026) : Stabilize API (no more removals)
                    : NoFileDescriptorLeak (unix)
                    : async: Eventually/Never to accept error and context, Consistently
                    : export internal tools (spew, difflib)
    section Q2 2026
    📝 v2.5 (May 2026) : New candidate features from upstream
                    : NoFileDescriptorLeak (windows port)
                    : export internal tools (blackbox)

Notes

  1. The first release comes with zero dependencies and an unstable API (see below our use case)
  2. This project is going to be injected as the main and sole test dependency of the go-openapi libraries
  3. Since we have leveled the go requirements to the rest of the go-openapi (currently go1.24) there is quite a bit of relinting lying ahead.
  4. Valuable pending pull requests from the original project could be merged (e.g. JSONEqBytes) or transformed as “enable” modules (e.g. colorized output)
  5. More testing and bug fixes (from upstream or detected during our testing)
  6. Introduces colorization (opt-in)
  7. Introduces generics
  8. Realign behavior re quirks, bugs, unexpected logics … (e.g. IsNonDecreasing, EventuallyWithT…)
  9. Unclear assertions might be provided an alternative verb (e.g. EventuallyWithT)

Adoption timeline at go-openapi

  1. Jan 2026: all go-openapi projects adopts the forked testify
  2. Feb 2026: all go-openapi projects transition to generics
  3. Mar 2026: go-swagger transitions to the forked testify

What won’t come anytime soon

  • mocks: we use mockery and prefer the simpler matryer mocking-style. testify-style mocks are thus not going to be supported anytime soon.
  • extra convoluted stuff in the like of InDeltaSlice (more likely to be removed)

Upstream Tracking

We actively monitor github.com/stretchr/testify for updates, new issues, and proposals.

Review frequency: Quarterly (next review: April 2026)

Processed items: 31 upstream PRs and issues have been reviewed, with 23 implemented/merged, 4 superseded by our implementation, 2 informational, and 2 currently under consideration.

For a complete catalog of all upstream PRs and issues we’ve processed (implemented, adapted, superseded, or monitoring), see the Upstream Tracking.

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Benchmarks

Last Updated: 2026-01-20

Overview

While the primary motivation for adding generic assertion functions to testify v2 was compile-time type safety (see Generics Guide for details), our benchmarking revealed an unexpected bonus: dramatic performance improvements ranging from 1.2x to 81x faster, with up to 99% reduction in memory allocations for collection operations.

This document focuses on the performance measurements and explains why these improvements occur.

Type Safety First, Performance Second

Generic assertions catch type errors when writing tests, not when running them. For example:

// Reflection: Compiles, fails at runtime
assert.ElementsMatch(t, []int{1, 2}, []string{"a", "b"})

// Generic: Compiler catches the error immediately
assert.ElementsMatchT(t, []int{1, 2}, []string{"a", "b"})  // ❌ Compile error!

See the Generics Guide for comprehensive coverage of type safety benefits, refactoring safety, and when to use generic vs reflection variants.

Performance Results by Category

🏆 Collection Operations: Exceptional Gains

Collection operations see the most dramatic improvements due to elimination of per-element reflection overhead:

FunctionSpeedupMemory ImpactWhy It Matters
ElementsMatch (10 items)21x faster568 B → 320 B (44% reduction)Common test operation
ElementsMatch (100 items)39x faster41 KB → 3.6 KB (91% reduction)Scales superlinearly
ElementsMatch (1000 items)81x faster4 MB → 33 KB (99% reduction)Large collection testing
SliceContains16x faster4 allocs → 0Membership testing
SeqContains (iter.Seq)25x faster55 allocs → 9Go 1.23+ iterators
SliceSubset43x faster17 allocs → 0Subset verification

Key insight: ElementsMatch’s O(n²) complexity amplifies the benefits—the speedup increases with collection size (21x → 39x → 81x).

⚡ Comparison Operations: Zero-Allocation Wins

Direct operator usage (>, <, ==) eliminates reflection overhead and boxing entirely:

FunctionSpeedupAllocationsBenchmark Data
Greater/Less10-15x faster1 → 0 allocs139.1ns → 17.9ns
Positive/Negative16-22x faster1 → 0 allocs121.5ns → 7.6ns
GreaterOrEqual/LessOrEqual10-11x faster1 → 0 allocsSimilar pattern
Equal10-13x faster0 allocs (both)44.8ns → 3.5ns
NotEqual11x faster0 allocs (both)Comparable to Equal

Key insight: Comparison operations are frequently used in tests. 10-15x speedup on common assertions accumulates quickly across large test suites.

📊 Ordering Operations: Eliminating Per-Element Overhead

Ordering checks iterate over collections, so eliminating per-element reflection creates significant gains:

FunctionSpeedupAllocation Impact
IsIncreasing7.4x faster11 allocs → 0
IsDecreasing9.5x faster11 allocs → 0
IsNonDecreasing8x faster4 allocs → 0
IsNonIncreasing6.5x faster4 allocs → 0

🔍 Type Checks: Cleaner API, Better Performance

Generic type checks eliminate reflection and provide a cleaner API:

FunctionSpeedupNotes
IsOfType9-11x fasterNo dummy value needed with generics
IsNotOfTypeSimilar gainsType parameter makes intent explicit

⚖️ Modest Gains: Where Processing Dominates

Some operations see smaller improvements because expensive processing dominates:

CategorySpeedupWhy Gains Are Limited
Same/NotSame1.5-2xPointer comparison already fast
Boolean checks~2xSimple bool comparison
JSONEqMarginalJSON parsing/unmarshaling dominates
RegexpMarginalRegex compilation dominates

Key insight: Even modest performance gains come with the benefit of compile-time type safety.

Understanding the Performance Gains

Allocation Elimination

The most dramatic speedups come from eliminating allocations entirely:

  • ElementsMatch (1000 elements): 501,503 → 3 allocations (99.999% reduction)
  • All comparison operations: 1 → 0 allocations
  • Ordering checks: 4-11 → 0 allocations

Less allocation pressure means faster execution and reduced GC overhead, especially impactful in large test suites.

Superlinear Scaling

For operations with O(n²) or O(n) complexity, eliminating per-element reflection overhead creates superlinear gains:

  • ElementsMatch: 21x (10 items) → 39x (100 items) → 81x (1000 items)
  • The speedup increases with collection size

Cumulative Impact

Test suites typically run thousands of assertions:

  • Small test suite (1,000 assertions): 10x average speedup = significantly faster CI runs
  • Large test suite (10,000+ assertions): Cumulative savings become substantial
  • Particularly valuable in CI/CD pipelines where test execution time directly affects deployment velocity

Sample Benchmark Data

Representative results from go test -bench=. ./internal/assertions:

# Collection operations
BenchmarkElementsMatch/reflect/1000-16   25.5 ms/op     4.0 MB/op   501503 allocs/op
BenchmarkElementsMatch/generic/1000-16    316 µs/op      33 KB/op        3 allocs/op
                                          ↑ 81x faster   ↑ 99% less memory

# Comparison operations
BenchmarkGreater/reflect/int-16          139.1 ns/op      34 B/op        1 allocs/op
BenchmarkGreater/generic/int-16           17.9 ns/op       0 B/op        0 allocs/op
                                          ↑ 7.8x faster

# Equality checks
BenchmarkEqual/reflect/int-16             44.8 ns/op       0 B/op        0 allocs/op
BenchmarkEqual/generic/int-16              3.5 ns/op       0 B/op        0 allocs/op
                                          ↑ 13x faster

Adopting Generic Assertions

See the Migration Guide for step-by-step instructions on migrating to generic assertions, and the Generics Guide for comprehensive coverage of type safety benefits and usage patterns.


Running Benchmarks

To run the benchmarks yourself:

go test -run=^$ -bench=. -benchmem ./internal/assertions

Benchmark Coverage

38/42 generic functions benchmarked across 10 domains:

  • Boolean (2): TrueT, FalseT
  • Collection (12): StringContainsT, SliceContainsT, MapContainsT, SeqContainsT, ElementsMatchT, SliceSubsetT, and negative variants
  • Comparison (6): GreaterT, LessT, GreaterOrEqualT, LessOrEqualT, PositiveT, NegativeT
  • Equality (4): EqualT, NotEqualT, SameT, NotSameT
  • JSON (1): JSONEqT
  • Number (2): InDeltaT, InEpsilonT
  • Ordering (6): IsIncreasingT, IsDecreasingT, IsNonIncreasingT, IsNonDecreasingT, SortedT, NotSortedT
  • String (2): RegexpT, NotRegexpT
  • Type (2): IsOfTypeT, IsNotOfTypeT
  • YAML (1): YAMLEqT (benchmarked separately in enable/yaml module)

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Original README

Testify - Thou Shalt Write Tests

Note

Testify is being maintained at v1, no breaking changes will be accepted in this repo.
See discussion about v2.

Build Status Go Report Card PkgGoDev

Go code (golang) set of packages that provide many tools for testifying that your code will behave as you intend.

Features include:

Get started:

assert package

The assert package provides some helpful methods that allow you to write better test code in Go.

  • Prints friendly, easy to read failure descriptions
  • Allows for very readable code
  • Optionally annotate each assertion with a message

See it in action:

package yours

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestSomething(t *testing.T) {
	// assert equality
	assert.Equal(t, 123, 123, "they should be equal")

	// assert inequality
	assert.NotEqual(t, 123, 456, "they should not be equal")

	// assert for nil (good for errors)
	assert.Nil(t, object)

	// assert for not nil (good when you expect something)
	if assert.NotNil(t, object) {
		// now we know that object isn't nil, we are safe to make
		// further assertions without causing any errors
		assert.Equal(t, "Something", object.Value)
	}
}
  • Every assert func takes the testing.T object as the first argument. This is how it writes the errors out through the normal go test capabilities.
  • Every assert func returns a bool indicating whether the assertion was successful or not, this is useful for if you want to go on making further assertions under certain conditions.

if you assert many times, use the below:

package yours

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestSomething(t *testing.T) {
	assert := assert.New(t)

	// assert equality
	assert.Equal(123, 123, "they should be equal")

	// assert inequality
	assert.NotEqual(123, 456, "they should not be equal")

	// assert for nil (good for errors)
	assert.Nil(object)

	// assert for not nil (good when you expect something)
	if assert.NotNil(object) {
		// now we know that object isn't nil, we are safe to make
		// further assertions without causing any errors
		assert.Equal("Something", object.Value)
	}
}

require package

The require package provides same global functions as the assert package, but instead of returning a boolean result they terminate current test. These functions must be called from the goroutine running the test or benchmark function, not from other goroutines created during the test. Otherwise race conditions may occur.

See t.FailNow for details.

mock package

The mock package provides a mechanism for easily writing mock objects that can be used in place of real objects when writing test code.

An example test function that tests a piece of code that relies on an external object testObj, can set up expectations (testify) and assert that they indeed happened:

package yours

import (
	"testing"

	"github.com/stretchr/testify/mock"
)

/*
  Test objects
*/

// MyMockedObject is a mocked object that implements an interface
// that describes an object that the code I am testing relies on.
type MyMockedObject struct {
	mock.Mock
}

// DoSomething is a method on MyMockedObject that implements some interface
// and just records the activity, and returns what the Mock object tells it to.
//
// In the real object, this method would do something useful, but since this
// is a mocked object - we're just going to stub it out.
//
// NOTE: This method is not being tested here, code that uses this object is.
func (m *MyMockedObject) DoSomething(number int) (bool, error) {
	args := m.Called(number)
	return args.Bool(0), args.Error(1)
}

/*
  Actual test functions
*/

// TestSomething is an example of how to use our test object to
// make assertions about some target code we are testing.
func TestSomething(t *testing.T) {
	// create an instance of our test object
	testObj := new(MyMockedObject)

	// set up expectations
	testObj.On("DoSomething", 123).Return(true, nil)

	// call the code we are testing
	targetFuncThatDoesSomethingWithObj(testObj)

	// assert that the expectations were met
	testObj.AssertExpectations(t)
}

// TestSomethingWithPlaceholder is a second example of how to use our test object to
// make assertions about some target code we are testing.
// This time using a placeholder. Placeholders might be used when the
// data being passed in is normally dynamically generated and cannot be
// predicted beforehand (eg. containing hashes that are time sensitive)
func TestSomethingWithPlaceholder(t *testing.T) {
	// create an instance of our test object
	testObj := new(MyMockedObject)

	// set up expectations with a placeholder in the argument list
	testObj.On("DoSomething", mock.Anything).Return(true, nil)

	// call the code we are testing
	targetFuncThatDoesSomethingWithObj(testObj)

	// assert that the expectations were met
	testObj.AssertExpectations(t)
}

// TestSomethingElse2 is a third example that shows how you can use
// the Unset method to cleanup handlers and then add new ones.
func TestSomethingElse2(t *testing.T) {
	// create an instance of our test object
	testObj := new(MyMockedObject)

	// set up expectations with a placeholder in the argument list
	mockCall := testObj.On("DoSomething", mock.Anything).Return(true, nil)

	// call the code we are testing
	targetFuncThatDoesSomethingWithObj(testObj)

	// assert that the expectations were met
	testObj.AssertExpectations(t)

	// remove the handler now so we can add another one that takes precedence
	mockCall.Unset()

	// return false now instead of true
	testObj.On("DoSomething", mock.Anything).Return(false, nil)

	testObj.AssertExpectations(t)
}

For more information on how to write mock code, check out the API documentation for the mock package.

You can use the mockery tool to autogenerate the mock code against an interface as well, making using mocks much quicker.

suite package

Warning

The suite package does not support parallel tests. See #934.

The suite package provides functionality that you might be used to from more common object-oriented languages. With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with ‘go test’ as per normal.

An example suite is shown below:

// Basic imports
import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
)

// Define the suite, and absorb the built-in basic suite
// functionality from testify - including a T() method which
// returns the current testing context
type ExampleTestSuite struct {
	suite.Suite
	VariableThatShouldStartAtFive int
}

// Make sure that VariableThatShouldStartAtFive is set to five
// before each test
func (suite *ExampleTestSuite) SetupTest() {
	suite.VariableThatShouldStartAtFive = 5
}

// All methods that begin with "Test" are run as tests within a
// suite.
func (suite *ExampleTestSuite) TestExample() {
	assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
}

// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestExampleTestSuite(t *testing.T) {
	suite.Run(t, new(ExampleTestSuite))
}

For a more complete example, using all of the functionality provided by the suite package, look at our example testing suite

For more information on writing suites, check out the API documentation for the suite package.

Suite object has assertion methods:

// Basic imports
import (
	"testing"

	"github.com/stretchr/testify/suite"
)

// Define the suite, and absorb the built-in basic suite
// functionality from testify - including assertion methods.
type ExampleTestSuite struct {
	suite.Suite
	VariableThatShouldStartAtFive int
}

// Make sure that VariableThatShouldStartAtFive is set to five
// before each test
func (suite *ExampleTestSuite) SetupTest() {
	suite.VariableThatShouldStartAtFive = 5
}

// All methods that begin with "Test" are run as tests within a
// suite.
func (suite *ExampleTestSuite) TestExample() {
	suite.Equal(suite.VariableThatShouldStartAtFive, 5)
}

// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestExampleTestSuite(t *testing.T) {
	suite.Run(t, new(ExampleTestSuite))
}

Installation

To install Testify, use go get:

go get github.com/stretchr/testify

This will then make the following packages available to you:

github.com/stretchr/testify/assert
github.com/stretchr/testify/require
github.com/stretchr/testify/mock
github.com/stretchr/testify/suite
github.com/stretchr/testify/http (deprecated)

Import the testify/assert package into your code using this template:

package yours

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestSomething(t *testing.T) {
	assert.True(t, true, "True is true!")
}

Staying up to date

To update Testify to the latest version, use go get -u github.com/stretchr/testify.


Supported go versions

We currently support the most recent major Go versions from 1.19 onward.


Contributing

Please feel free to submit issues, fork the repository and send pull requests!

When submitting an issue, we ask that you please include a complete test function that demonstrates the issue. Extra credit for those using Testify to write the test code that demonstrates it.

Code generation is used. Look for Code generated with at the top of some files. Run go generate ./... to update generated files.

We also chat on the Gophers Slack group in the #testify and #testify-dev channels.


License

This project is licensed under the terms of the MIT license.

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

Motivation

From the maintainers of testify, it looks like a v2 will eventually be released, but they’ll do it at their own pace.

We like all the principles they exposed to build this v2. See discussion about v2.

However, at go-openapi we would like to address the well-known issues in testify with different priorities.

With this fork, we want to:

  1. remove all external dependencies.
  2. make it easy to maintain and extend.
  3. pare down some of the chrome that has been added over the years.

Extended hand

We hope that this endeavor will help the original project with a live-drill of what a v2 could look like.

Hopefully, some of our ideas will eventually percolate back into the original project and help the wider community of go developers write better, clearer test code.

Feedback is welcome and we are always happy to discuss with people who face the same problems as we do: avoid breaking changes, APIs that became bloated over a decade or so, uncontrolled dependencies, difficult choices when it comes to introduce breaking changes, conflicting demands from users etc.

You might also be curious about our ROADMAP.


  1. We wanted first to remove all external dependencies.

For all our libraries and generated test code we don’t want test dependencies to drill farther than import github.com/go-openapi/testify/v2, but on some specific (and controlled) occasions.

In this fork, all external stuff is either internalized (go-spew, difflib), removed (mocks, suite, http) or specifically enabled by importing this module (github.com/go-openapi/testify/enable/yaml/v2).

  1. Make it easy to maintain and extend.

For go-openapi, testify should just be yet another part of our toolkit. We need it to work, be easily adaptable to our needs and not divert our development effort away from our other repos. This big refactor is an investment.

  1. We want to pare down some of the chrome that has been added over the years

The go-openapi libraries and the go-swagger project make a rather limited use of the vast API provided by testify.

With this first version of the fork, we have removed mocks and suite, which we don’t use. They might be added later on, with better controlled dependencies.

In the forthcoming maintenance of this fork, much of the “chrome” or “ambiguous” API will be pared down. There is no commitment yet on the stability of the API.

Chrome would be added later: we have the “enable” packages just for that for when external dependencies are needed.

  1. We want to add new features like generics, more useful assertions for JSON and safety checks.

  2. We want to get rid of the API quirks and gotchas that panic or return unexpected results.

Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

LICENSE

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

NOTICE

// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0

This software library, github.com/go-openapi/testify, includes software developed
by the go-swagger and go-openapi maintainers ("go-swagger maintainers").

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this software except in compliance with the License.
You may obtain a copy of the License at

This software is copied from, derived from, and inspired mainly by github.com/stretchr/testify.
It ships with copies of other software which license terms are recalled below.


github.com/stretchr/testify
===========================

// SPDX-FileCopyrightText: Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.
// SPDX-License-Identifier: MIT

MIT License

Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

github.com/davecgh/go-spew
===========================

// SPDX-FileCopyrightText: Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
// SPDX-License-Identifier: ISC

ISC License

Copyright (c) 2012-2016 Dave Collins <dave@davec.name>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

github.com/pmezard/go-difflib
===========================

// SPDX-FileCopyrightText: Copyright (c) 2013, Patrick Mezard
// SPDX-License-Identifier: MIT-like

Copyright (c) 2013, Patrick Mezard
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
    Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
    The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Last edited by: fredbi Feb 20, 2026
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license.

An approach to testing

TL;DR

Software testing comes in two major styles: assertion-style (xUnit tradition: JUnit, pytest, NUnit, testify) and BDD-style (RSpec, Cucumber, Ginkgo).

Both are valid approaches. However, we think that testify’s assertion-style naturally aligns with Go’s core values: simplicity, explicitness, standard library first, minimal abstraction.

Testify brings powerful testing to Go developers who embrace these values: zero-dependencies, reflection-based or generic assertions, and no framework (just works with go test).

If you chose Go for its philosophy, assertion-style testing is the natural extension of those values to your test suite.


Make testing better. Keep it Go

go-openapi/testify follows a simple philosophy: make Go testing better without reinventing it.

Testify follows the assertion style: it is not a BDD framework. So you won’t find chaining methods that produce English-like sentences.

Unlike frameworks that introduce new paradigms and require specialized tooling, testify builds directly on top of Go’s great standard testing package.

It provides powerful assertions and utilities while preserving the familiar patterns that Go developers already know. Testing patterns and constructs remain standard.

Core Principles

1. Zero Dependencies

Testify has no external dependencies. Everything you need is self-contained, with internalized implementations of required functionality. This means:

  • No dependency conflicts in your project
  • No supply chain security concerns
  • No version compatibility issues
  • Chrome is opt-in (all extra features that need additional dependencies are opt-in)

2. Standard Go Compatibility

Works seamlessly with go test and the standard library:

  • No special CLI tools required
  • No framework-specific test runners
  • Standard Go subtests with t.Run()
  • Native IDE support out of the box
  • Works with any Go test runner

3. Type Safety with Generics

Testify embraces Go’s type system:

  • Most assertions come with a generic variant for compile-time type safety
  • Catch type mismatches before tests even run
  • On average 10x faster than reflection-based assertions
  • Full type inference: no manual type parameters needed
  • Complex cases that require dynamic typing use go reflection

4. Simplicity and Clarity

Keep testing straightforward:

  • Function-based assertions with clear semantics
  • No new DSL to learn
  • Minimal cognitive overhead
  • Immediate productivity for any Go developer

Testing Styles: Assertion vs. BDD

Software testing has evolved into two primary styles, each with passionate advocates across programming communities.

Assertion-Style Testing (xUnit tradition)

Core idea: Write tests as regular code with explicit assertions.

Originating with Kent Beck’s SUnit (Smalltalk) and popularized by JUnit (Java), this style emphasizes:

  • Tests are functions/methods in the language
  • Direct assertion calls verify behavior
  • Standard language constructs for organization
  • Minimal framework abstraction

Examples across languages:

// JUnit (Java)
@Test
public void testUserCreation() {
    User user = createUser("alice@example.com");
    assertNotNull(user);
    assertEquals("alice@example.com", user.getEmail());
}
# pytest (Python)
def test_user_creation():
    user = create_user("alice@example.com")
    assert user is not None
    assert user.email == "alice@example.com"
// NUnit (C#)
[Test]
public void TestUserCreation()
{
    var user = CreateUser("alice@example.com");
    Assert.IsNotNull(user);
    Assert.AreEqual("alice@example.com", user.Email);
}
// Testify (go)
import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

func TestUserCreation(t *testing.T) {
    user := CreateUser("alice@example.com")
    require.NotNil(t, user)
    assert.Equal(t, "alice@example.com", user.Email)
}

Frameworks: JUnit, NUnit, xUnit.net, pytest, PHPUnit, Go’s testing package… and testify.

BDD-Style Testing (Behavior-Driven Development)

Core idea: Write tests as executable specifications in narrative form.

Originating with RSpec (Ruby) and influenced by Dan North’s BDD methodology, this style emphasizes:

  • Tests describe behavior in natural language structure
  • Hierarchical organization (describe/context/it)
  • Focus on readability and documentation value
  • Framework-specific DSL

Examples across languages:

# RSpec (Ruby)
describe "User creation" do
  it "creates a valid user" do
    user = create_user("alice@example.com")
    expect(user).not_to be_nil
    expect(user.email).to eq("alice@example.com")
  end
end
// Jasmine/Mocha (JavaScript)
describe("User creation", function() {
  it("creates a valid user", function() {
    const user = createUser("alice@example.com");
    expect(user).not.toBe(null);
    expect(user.email).toEqual("alice@example.com");
  });
});
# behave (Python)
Scenario: User creation
  Given a valid email address
  When I create a user with "alice@example.com"
  Then the user should exist
  And the email should be "alice@example.com"
// Ginkgo
import (
    "github.com/onsi/ginkgo/v2"
    "github.com/onsi/gomega"
)

var _ = ginkgo.Describe("User creation", func() {
            ginkgo.It("creates a valid user", func() {
                user := CreateUser("alice@example.com")
                gomega.Expect(user).NotTo(gomega.BeNil())
                gomega.Expect(user.Email).To(gomega.Equal("alice@example.com"))
            })
        })

Frameworks: RSpec, Jasmine, Mocha, Cucumber, behave, Ginkgo/Gomega

Both Are Valid

Assertion-style strengths:

  • Low cognitive overhead (just code)
  • Minimal framework abstraction
  • IDE tooling works naturally
  • Easy to learn and adopt

BDD-style strengths:

  • Readable test specifications
  • Natural hierarchical organization
  • Self-documenting intent
  • Stakeholder-friendly output

The debate continues across all programming communities. Neither style is objectively superior; they optimize for different values and team preferences.


Assertion-Style and Go Values

While both styles have merit in general, assertion-style testing aligns naturally with Go’s core philosophy.

Go’s Design Values

Go emphasizes:

  • Simplicity: maximize clarity
  • Explicitness: No magic, no hidden behavior
  • Standard library first: Build on solid foundations
  • Readability: Code is read more than written
  • Minimal abstraction: Minimize concepts

How Assertion-Style Matches Go

1. Simplicity

Assertion-style keeps tests simple: they’re just Go functions.

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
	"github.com/go-openapi/testify/v2/require"
)

func TestAdd(t *testing.T) {
	result := Add(2, 3)
	assert.Equal(t, 5, result) // Straightforward, even though we haven't written "When().Two().Plus().Three().IsNot(5).Fail()"...
}

No new mental model. No framework semantics to learn. If you know Go, you know how to test with our lib.

2. Explicitness

Every assertion is an explicit function call with clear semantics:

assert.NotNil(t, user)                     // Explicit: check for nil
assert.ErrorIs(t, err, ErrNotFound)        // Explicit: check error identity
assert.ElementsMatch(t, expected, actual)  // Explicit: check collection equality

Compare to matcher-based approaches where behavior is composed through framework objects. Assertion-style makes test intent immediately clear to a programmer.

3. Standard Library First

Testify builds on testing.T: no replacement, just enhancement.

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUserAPI(t *testing.T) {
	t.Run("creation", func(t *testing.T) { // Standard Go subtest
		t.Parallel() // Standard Go test parallelism

		user := CreateUser("alice@example.com")
		assert.NotNil(t, user) // Enhanced with assertions
	})
}

Works with go test. Works with standard tooling. Works with the Go ecosystem.

4. Readability Through Directness

Go prioritizes code that’s easy to read and understand:

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

// Clear, direct, readable
func TestEmailValidation(t *testing.T) {
	tests := []struct {
		email string
		valid bool
	}{
		{"alice@example.com", true},
		{"invalid-email", false},
	}

	for _, tt := range tests {
		t.Run(tt.email, func(t *testing.T) {
			err := ValidateEmail(tt.email)
			if tt.valid {
				assert.NoError(t, err)

                return
			}

			assert.Error(t, err)
		})
	}
}

Standard control flow. Standard Go idioms. No DSL to decode. Better for most developers, perhaps less so for stakeholders not familiar with Go.

5. Minimal Abstraction

Go avoids abstraction for abstraction’s sake. Testify provides assertions: nothing more.

  • No test lifecycle framework
  • No dependency injection system
  • No specialized runners
  • No mandatory patterns

Just functions that verify behavior and produce clear errors. Solve the testing problem, don’t create a testing ecosystem.

The Natural Fit

If you appreciate Go’s philosophy and if you choose Go because you value simplicity, explicitness, and building on standards, then assertion-style testing is the natural extension of those values to your test suite.

BDD frameworks serve teams with different priorities (narrative specifications, framework-managed workflows, stakeholder communication). Those are valid priorities. But they optimize for values orthogonal to Go’s design philosophy. For such teams, Ginkgo/Gomega provides a great BDD testing framework.

For Go developers who embrace Go values, assertion-style testing is the idiomatic approach.. And testify is the tool.


Assertion-Style in Go: Testify vs. BDD Frameworks

Go’s testing ecosystem reflects the broader assertion-vs-BDD divide:

Different Philosophies

AspectTestify (Assertion-Style)Ginkgo/Gomega (BDD-Style)
Testing stylexUnit traditionBDD tradition
ApproachEnhance standard testingReplace with BDD framework
IntegrationWorks with go test directlyRequires ginkgo CLI tool
Learning curveImmediate (standard Go)Moderate (new DSL)
DependenciesZero external packagesMultiple framework packages
Type safetyGeneric assertionsReflection-based matchers
OrganizationStandard Go subtestsNarrative hierarchy (Describe/Context/It)
Go philosophyAligns with Go valuesDifferent priorities

Example Comparison

Assertion-style (Testify):

import (
	"testing"

	"github.com/go-openapi/testify/v2/assert"
)

func TestUserCreation(t *testing.T) {
	t.Parallel()

	user := CreateUser("alice@example.com")

	// Clear, type-safe assertions
	require.NotNil(t, user)
	assert.EqualT(t, "alice@example.com", user.Email) // Compile-time type check
	assert.True(t, user.Active)
}

BDD-style (Ginkgo/Gomega):

var _ = Describe("User Creation", func() {
	var user *User

	BeforeEach(func() {
		user = CreateUser("alice@example.com")
	})

	It("creates a valid user", func() {
		Expect(user).ToNot(BeNil())
		Expect(user.Email).To(Equal("alice@example.com"))
		Expect(user.Active).To(BeTrue())
	})
})

Both approaches are valid. They reflect different testing philosophies that span the entire software industry. The question for Go developers is: which style aligns with the values that drew you to Go in the first place?


See also