Project roadmap, contributing guidelines and maintainer’s guide.
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.
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).
// real-world test would inject *testing.T from TestFalse(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestFalse(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestFalse(t *testing.T)require.False(t, 1==0)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestFalseT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestFalseT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestFalseT(t *testing.T)require.FalseT(t, 1==0)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestTrue(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestTrue(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestTrue(t *testing.T)require.True(t, 1==1)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestTrueT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestTrueT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestTrueT(t *testing.T)require.TrueT(t, 1==1)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestContains(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestContains(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 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.
// real-world test would inject *testing.T from TestElementsMatch(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 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.
// real-world test would inject *testing.T from TestElementsMatchT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestLen(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestLen(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestMapContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestMapContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestMapNotContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestMapNotContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestNotContains(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotContains(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 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.
// real-world test would inject *testing.T from TestNotElementsMatch(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotElementsMatch(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 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.
// real-world test would inject *testing.T from TestNotElementsMatchT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotElementsMatchT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 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.
// real-world test would inject *testing.T from TestNotSubset(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotSubset(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestSeqContainsT(t *testing.T)packagemainimport (
"fmt""slices""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestSeqContainsT(t *testing.T)packagemainimport (
"fmt""slices""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestSeqNotContainsT(t *testing.T)packagemainimport (
"fmt""slices""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestSeqNotContainsT(t *testing.T)packagemainimport (
"fmt""slices""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestSliceContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestSliceContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestSliceNotContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestSliceNotContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestSliceNotSubsetT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestSliceNotSubsetT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestSliceSubsetT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestSliceSubsetT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestStringContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestStringContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestStringContainsT(t *testing.T)require.StringContainsT(t, "AB", "A")
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestStringNotContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestStringNotContainsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestStringNotContainsT(t *testing.T)require.StringNotContainsT(t, "AB", "C")
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestSubset(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestSubset(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
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.
// real-world test would inject *testing.T from TestGreater(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestGreater(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestGreater(t *testing.T)require.Greater(t, 2, 1)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestGreaterOrEqual(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestGreaterOrEqual(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestGreaterOrEqual(t *testing.T)require.GreaterOrEqual(t, 2, 1)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestGreaterOrEqualT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestGreaterOrEqualT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestGreaterOrEqualT(t *testing.T)require.GreaterOrEqualT(t, 2, 1)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestGreaterT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestGreaterT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestGreaterT(t *testing.T)require.GreaterT(t, 2, 1)
fmt.Println("passed")
}
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.
// real-world test would inject *testing.T from TestLess(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestLess(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestLess(t *testing.T)require.Less(t, 1, 2)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestLessOrEqual(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestLessOrEqual(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestLessOrEqual(t *testing.T)require.LessOrEqual(t, 1, 2)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestLessOrEqualT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestLessOrEqualT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestLessOrEqualT(t *testing.T)require.LessOrEqualT(t, 1, 2)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestLessT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestLessT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestLessT(t *testing.T)require.LessT(t, 1, 2)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNegative(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNegative(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNegative(t *testing.T)require.Negative(t, -1)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNegativeT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNegativeT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNegativeT(t *testing.T)require.NegativeT(t, -1)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestPositive(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestPositive(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestPositive(t *testing.T)require.Positive(t, 1)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestPositiveT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestPositiveT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestPositiveT(t *testing.T)require.PositiveT(t, 1)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestCondition(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestCondition(t *testing.T)success:=assert.Condition(t, func() bool {
returntrue })
fmt.Printf("success: %t\n", success)
}
// real-world test would inject *testing.T from TestCondition(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestCondition(t *testing.T)require.Condition(t, func() bool {
returntrue })
fmt.Println("passed")
}
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.
// real-world test would inject *testing.T from TestConsistently(t *testing.T)packagemainimport (
"fmt""testing""time""github.com/go-openapi/testify/v2/assert")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestConsistently(t *testing.T)success:=assert.Consistently(t, func() bool {
returntrue }, 100*time.Millisecond, 20*time.Millisecond)
fmt.Printf("success: %t\n", success)
}
// real-world test would inject *testing.T from TestConsistently(t *testing.T)packagemainimport (
"fmt""sync/atomic""testing""time""github.com/go-openapi/testify/v2/assert")
funcmain() {
t:= new(testing.T) // normally provided by test// A counter that stays within bounds during the test.varcounteratomic.Int32counter.Store(5)
result:=assert.Consistently(t, func() bool {
returncounter.Load() < 10 }, 100*time.Millisecond, 10*time.Millisecond)
fmt.Printf("consistently under limit: %t", result)
}
// real-world test would inject *testing.T from TestConsistently(t *testing.T)packagemainimport (
"fmt""testing""time""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestConsistently(t *testing.T)require.Consistently(t, func() bool {
returntrue }, 100*time.Millisecond, 20*time.Millisecond)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestConsistently(t *testing.T)packagemainimport (
"fmt""sync/atomic""testing""time""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // normally provided by test// A counter that stays within bounds during the test.varcounteratomic.Int32counter.Store(5)
require.Consistently(t, func() bool {
returncounter.Load() < 10 }, 100*time.Millisecond, 10*time.Millisecond)
fmt.Printf("consistently under limit: %t", !t.Failed())
}
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”
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).
// real-world test would inject *testing.T from TestEventually(t *testing.T)packagemainimport (
"fmt""testing""time""github.com/go-openapi/testify/v2/assert")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestEventually(t *testing.T)success:=assert.Eventually(t, func() bool {
returntrue }, 100*time.Millisecond, 20*time.Millisecond)
fmt.Printf("success: %t\n", success)
}
// real-world test would inject *testing.T from TestEventually(t *testing.T)packagemainimport (
"fmt""sync/atomic""testing""time""github.com/go-openapi/testify/v2/assert")
funcmain() {
t:= new(testing.T) // normally provided by test// Simulate an async operation that completes after a short delay.varreadyatomic.Boolgofunc() {
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)
}
// real-world test would inject *testing.T from TestEventually(t *testing.T)packagemainimport (
"context""errors""fmt""sync/atomic""testing""time""github.com/go-openapi/testify/v2/assert")
funcmain() {
t:= new(testing.T) // normally provided by test// Simulate a service that becomes healthy after a few attempts.varattemptsatomic.Int32healthCheck:=func(_context.Context) error {
ifattempts.Add(1) < 3 {
returnerrors.New("service not ready")
}
returnnil }
result:=assert.Eventually(t, healthCheck, 200*time.Millisecond, 10*time.Millisecond)
fmt.Printf("eventually healthy: %t", result)
}
// real-world test would inject *testing.T from TestEventually(t *testing.T)packagemainimport (
"fmt""testing""time""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestEventually(t *testing.T)require.Eventually(t, func() bool {
returntrue }, 100*time.Millisecond, 20*time.Millisecond)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestEventually(t *testing.T)packagemainimport (
"fmt""sync/atomic""testing""time""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // normally provided by test// Simulate an async operation that completes after a short delay.varreadyatomic.Boolgofunc() {
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())
}
// real-world test would inject *testing.T from TestEventually(t *testing.T)packagemainimport (
"context""errors""fmt""sync/atomic""testing""time""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // normally provided by test// Simulate a service that becomes healthy after a few attempts.varattemptsatomic.Int32healthCheck:=func(_context.Context) error {
ifattempts.Add(1) < 3 {
returnerrors.New("service not ready")
}
returnnil }
require.Eventually(t, healthCheck, 200*time.Millisecond, 10*time.Millisecond)
fmt.Printf("eventually healthy: %t", !t.Failed())
}
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:=falsegofunc() {
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 tickassertions.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.Millisecondfailure: func(c*CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond
// real-world test would inject *testing.T from TestEventuallyWith(t *testing.T)packagemainimport (
"fmt""testing""time""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestEventuallyWith(t *testing.T)packagemainimport (
"fmt""testing""time""github.com/go-openapi/testify/v2/assert""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestNever(t *testing.T)packagemainimport (
"fmt""testing""time""github.com/go-openapi/testify/v2/assert")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNever(t *testing.T)success:=assert.Never(t, func() bool {
returnfalse }, 100*time.Millisecond, 20*time.Millisecond)
fmt.Printf("success: %t\n", success)
}
// real-world test would inject *testing.T from TestNever(t *testing.T)packagemainimport (
"fmt""testing""time""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNever(t *testing.T)require.Never(t, func() bool {
returnfalse }, 100*time.Millisecond, 20*time.Millisecond)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestEmpty(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestEmpty(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestEmpty(t *testing.T)require.Empty(t, "")
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestEqual(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestEqual(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestEqual(t *testing.T)require.Equal(t, 123, 123)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestEqualT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestEqualT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestEqualT(t *testing.T)require.EqualT(t, 123, 123)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestEqualValues(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestEqualValues(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestExactly(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestExactly(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestNil(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNil(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNil(t *testing.T)require.Nil(t, nil)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNotEmpty(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotEmpty(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNotEmpty(t *testing.T)require.NotEmpty(t, "not empty")
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNotEqual(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotEqual(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNotEqual(t *testing.T)require.NotEqual(t, 123, 456)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNotEqualT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotEqualT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNotEqualT(t *testing.T)require.NotEqualT(t, 123, 456)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNotEqualValues(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotEqualValues(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestNotNil(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotNil(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNotNil(t *testing.T)require.NotNil(t, "not nil")
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNotSame(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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 testingvarstaticVar = "static string"funcptr[Tany](valueT) *T {
p:=valuereturn&p}
// real-world test would inject *testing.T from TestNotSame(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 testingvarstaticVar = "static string"funcptr[Tany](valueT) *T {
p:=valuereturn&p}
// real-world test would inject *testing.T from TestNotSameT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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 testingvarstaticVar = "static string"funcptr[Tany](valueT) *T {
p:=valuereturn&p}
// real-world test would inject *testing.T from TestNotSameT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 testingvarstaticVar = "static string"funcptr[Tany](valueT) *T {
p:=valuereturn&p}
// real-world test would inject *testing.T from TestSame(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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 testingvar (
staticVar = "static string"staticVarPtr = &staticVar)
// real-world test would inject *testing.T from TestSame(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 testingvar (
staticVar = "static string"staticVarPtr = &staticVar)
// real-world test would inject *testing.T from TestSameT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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 testingvar (
staticVar = "static string"staticVarPtr = &staticVar)
// real-world test would inject *testing.T from TestSameT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 testingvar (
staticVar = "static string"staticVarPtr = &staticVar)
// real-world test would inject *testing.T from TestEqualError(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestEqualError(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestError(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestError(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestError(t *testing.T)require.Error(t, require.ErrTest)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestErrorContains(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestErrorContains(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestErrorIs(t *testing.T)packagemainimport (
"fmt""io""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestErrorIs(t *testing.T)packagemainimport (
"fmt""io""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestNoError(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNoError(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNoError(t *testing.T)require.NoError(t, nil)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNotErrorIs(t *testing.T)packagemainimport (
"fmt""io""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotErrorIs(t *testing.T)packagemainimport (
"fmt""io""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
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.
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.
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.
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.
// real-world test would inject *testing.T from TestHTTPError(t *testing.T)packagemainimport (
"fmt""net/http""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestHTTPError(t *testing.T)require.HTTPError(t, httpError, "GET", "/", nil)
fmt.Println("passed")
}
funchttpError(whttp.ResponseWriter, _*http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}
// real-world test would inject *testing.T from TestHTTPRedirect(t *testing.T)packagemainimport (
"fmt""net/http""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestHTTPRedirect(t *testing.T)require.HTTPRedirect(t, httpRedirect, "GET", "/", nil)
fmt.Println("passed")
}
funchttpRedirect(whttp.ResponseWriter, _*http.Request) {
w.WriteHeader(http.StatusTemporaryRedirect)
}
// real-world test would inject *testing.T from TestHTTPSuccess(t *testing.T)packagemainimport (
"fmt""net/http""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestHTTPSuccess(t *testing.T)require.HTTPSuccess(t, httpOK, "GET", "/", nil)
fmt.Println("passed")
}
funchttpOK(whttp.ResponseWriter, _*http.Request) {
w.WriteHeader(http.StatusOK)
}
// real-world test would inject *testing.T from TestJSONEq(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestJSONEqBytes(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestJSONEqT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestJSONMarshalAsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
typedummyStructstruct {
Astringbint}
// real-world test would inject *testing.T from TestJSONMarshalAsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
typedummyStructstruct {
Astringbint}
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.
// real-world test would inject *testing.T from TestJSONUnmarshalAsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
typedummyStructstruct {
Astringbint}
// real-world test would inject *testing.T from TestJSONUnmarshalAsT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
typedummyStructstruct {
Astringbint}
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)
// real-world test would inject *testing.T from TestInDelta(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestInDelta(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestInDeltaMapValues(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestInDeltaMapValues(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestInDeltaSlice(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestInDeltaSlice(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestInDeltaT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestInDeltaT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestInEpsilon(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestInEpsilon(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestInEpsilonSlice(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestInEpsilonSlice(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 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.
// real-world test would inject *testing.T from TestInEpsilonT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestInEpsilonT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestIsDecreasing(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestIsDecreasing(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestIsDecreasingT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestIsDecreasingT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestIsIncreasing(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestIsIncreasing(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestIsIncreasingT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestIsIncreasingT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestIsNonDecreasing(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestIsNonDecreasing(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestIsNonDecreasingT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestIsNonDecreasingT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestIsNonIncreasing(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestIsNonIncreasing(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestIsNonIncreasingT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestIsNonIncreasingT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestNotSortedT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotSortedT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestSortedT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestSortedT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestNotPanics(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotPanics(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNotPanics(t *testing.T)require.NotPanics(t, func() {
})
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestPanics(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestPanics(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 asserts that the code inside the specified function panics,
and that the recovered panic value is an error that satisfies the EqualError comparison.
// real-world test would inject *testing.T from TestPanicsWithError(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestPanicsWithError(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestPanicsWithValue(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestPanicsWithValue(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
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() {}
// real-world test would inject *testing.T from TestNoFileDescriptorLeak(t *testing.T)packagemainimport (
"fmt""runtime""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
ifruntime.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)
}
// real-world test would inject *testing.T from TestNoFileDescriptorLeak(t *testing.T)packagemainimport (
"fmt""runtime""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
ifruntime.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 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.
// real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)packagemainimport (
"fmt""sync""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
t:= new(testing.T) // normally provided by testblocker:= make(chanstruct{})
varwgsync.WaitGroupdeferfunc() {
// 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 leakgofunc() {
deferwg.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:30fmt.Printf("has leak: %t", !result)
}
// real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNoGoRoutineLeak(t *testing.T)require.NoGoRoutineLeak(t, func() {
})
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers// SPDX-License-Identifier: Apache-2.0packagemainimport (
"fmt""sync""github.com/go-openapi/testify/v2/require")
funcmain() {
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(chanstruct{})
varwgsync.WaitGroupdeferfunc() {
// 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 leakgofunc() {
deferwg.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:30fmt.Printf("passed: %t", !t.Failed())
}
typemockFailNowTstruct {
failedbool}
// Helper is like [testing.T.Helper] but does nothing.func (mockFailNowT) Helper() {}
func (m*mockFailNowT) Errorf(formatstring, args...any) {
_ = format_ = args}
func (m*mockFailNowT) FailNow() {
m.failed = true}
func (m*mockFailNowT) Failed() bool {
returnm.failed}
// real-world test would inject *testing.T from TestNotRegexp(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotRegexp(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNotRegexp(t *testing.T)require.NotRegexp(t, "^start", "not starting")
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNotRegexpT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotRegexpT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNotRegexpT(t *testing.T)require.NotRegexpT(t, "^start", "not starting")
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestRegexp(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestRegexp(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestRegexp(t *testing.T)require.Regexp(t, "^start", "starting")
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestRegexpT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestRegexpT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestRegexpT(t *testing.T)require.RegexpT(t, "^start", "starting")
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestImplements(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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 testingvar (
staticVar = "static string"dummyInterfaceassert.T)
funcptr[Tany](valueT) *T {
p:=valuereturn&p}
// real-world test would inject *testing.T from TestImplements(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 testingvar (
staticVar = "static string"dummyInterfacerequire.T)
funcptr[Tany](valueT) *T {
p:=valuereturn&p}
// real-world test would inject *testing.T from TestIsNotOfTypeT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
typemyTypefloat64
// real-world test would inject *testing.T from TestIsNotOfTypeT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestIsNotOfTypeT(t *testing.T)require.IsNotOfTypeT[myType](t, 123.123)
fmt.Println("passed")
}
typemyTypefloat64
// real-world test would inject *testing.T from TestIsNotType(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestIsNotType(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
// real-world test would inject *testing.T from TestIsOfTypeT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
typemyTypefloat64
// real-world test would inject *testing.T from TestIsOfTypeT(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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")
}
typemyTypefloat64
// real-world test would inject *testing.T from TestIsType(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestIsType(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestIsType(t *testing.T)require.IsType(t, 123, 456)
fmt.Println("passed")
}
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.
// real-world test would inject *testing.T from TestKind(t *testing.T)packagemainimport (
"fmt""reflect""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestKind(t *testing.T)packagemainimport (
"fmt""reflect""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestKind(t *testing.T)require.Kind(t, reflect.String, "hello")
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNotImplements(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotImplements(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
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 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.
// real-world test would inject *testing.T from TestNotKind(t *testing.T)packagemainimport (
"fmt""reflect""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotKind(t *testing.T)packagemainimport (
"fmt""reflect""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNotKind(t *testing.T)require.NotKind(t, reflect.String, 0)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestNotZero(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestNotZero(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestNotZero(t *testing.T)require.NotZero(t, 1)
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestZero(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/assert")
funcmain() {
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)
}
// real-world test would inject *testing.T from TestZero(t *testing.T)packagemainimport (
"fmt""testing""github.com/go-openapi/testify/v2/require")
funcmain() {
t:= new(testing.T) // should come from testing, e.g. func TestZero(t *testing.T)require.Zero(t, 0)
fmt.Println("passed")
}
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)
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.
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.
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:
Domain
Examples
Count
Boolean
True, False
2
Equality
Equal, NotEqual, EqualValues, Same, Exactly
8
Comparison
Greater, Less, Positive
8
Collection
Contains, Len, Empty, ElementsMatch
18
Error
Error, NoError, ErrorIs, ErrorAs, ErrorContains
8
Type
IsType, Implements, Zero
7
String
Regexp, NotRegexp
4
Numeric
InDelta, InEpsilon
6
Ordering
IsIncreasing, Sorted
8
Panic
Panics, NotPanics
4
Others
HTTP, JSON, YAML, Time, File assertions
12
Browse by Domain
See the complete API Reference organized by domain for a detailed documentation of all assertions.
Navigating the Documentation
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
Browse the API Reference by domain (e.g., “Collection” for slice operations)
Use when: Test cannot continue meaningfully after failure
import (
"testing""github.com/go-openapi/testify/v2/require")
funcTestUser(t*testing.T) {
user:=getUser()
require.NotNil(t, user) // ✓ Calls t.FailNow() if failsrequire.Equal(t, "Alice", user.Name) // Safe to proceedrequire.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:
Pattern
Example
Description
Base
Equal(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:
Assertion
Inverse
Pattern
Equal
NotEqual
Not prefix
Nil
NotNil
Not prefix
Empty
NotEmpty
Not prefix
Contains
NotContains
Not prefix
Zero
NotZero
Not prefix
Same
NotSame
Not prefix
Panics
NotPanics
Not prefix
Regexp
NotRegexp
Not prefix
Exceptions: Some assertions use semantic opposites instead of the Not prefix:
Pattern 2: Multiple Assertions (assert for context)
import (
"testing""github.com/go-openapi/testify/v2/assert")
funcTestUserValidation(t*testing.T) {
user:=createUser()
// Use assert to see all failuresassert.NotEmpty(t, user.Name) // Check nameassert.NotEmpty(t, user.Email) // Check emailassert.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")
funcTestDatabaseQuery(t*testing.T) {
db:=connectDB()
require.NotNil(t, db) // Stop if no connectionresult:=db.Query("SELECT * FROM users")
require.NoError(t, result.Error) // Stop if query fails// Safe to proceed - db and result are validassert.NotEmpty(t, result.Rows)
}
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:
internal/assertions/yaml.go calls yaml.Unmarshal() - an abstraction layer
The abstraction layer panics if no unmarshaler is registered
The enable/yaml/v2 package registers gopkg.in/yaml.v3 via init() when imported (e.g. on blank import)
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:
packagetestutilimport (
goccyyaml"github.com/goccy/go-yaml"yamlstub"github.com/go-openapi/testify/v2/enable/stubs/yaml")
funcinit() {
// Register goccy/go-yaml as the YAML unmarshaleryamlstub.EnableYAMLWithUnmarshal(goccyyaml.Unmarshal)
}
Then import your custom enable package in your tests:
// File: mypackage/user_test.gopackagemypackageimport (
"testing"_"yourmodule/internal/testutil"// Register goccy/go-yaml"github.com/go-openapi/testify/v2/assert")
funcTestUserYAML(t*testing.T) {
expected:=`
name: Alice
email: alice@example.com
age: 30
`actual:=serializeUser(getUser())
// Now uses goccy/go-yaml under the hoodassert.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
Register once: Call EnableYAMLWithUnmarshal() only once, typically in an init() function
Not concurrent-safe: The registration is global and should happen during package or main program initialization
Signature compatibility: The custom unmarshaler must match the signature func([]byte, any) error
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:
packagetestutilimport (
"fmt""log"goccyyaml"github.com/goccy/go-yaml"yamlstub"github.com/go-openapi/testify/v2/enable/stubs/yaml")
funcinit() {
// Wrap the unmarshaler to add logging or validationyamlstub.EnableYAMLWithUnmarshal(func(data []byte, vany) error {
// Custom pre-processingif len(data) ==0 {
returnfmt.Errorf("empty YAML document")
}
// Call the actual unmarshalererr:=goccyyaml.Unmarshal(data, v)
// Custom post-processingiferr!=nil {
log.Printf("YAML unmarshal error: %v", err)
}
returnerr })
}
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
Use assert when you want tests to continue after a failure:
import (
"testing""github.com/go-openapi/testify/v2/assert")
funcTestUser(t*testing.T) {
user:=GetUser(123)
// All three checks run, even if the first failsassert.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")
funcTestUser(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 nilassert.Equal(t, "Alice", user.Name)
}
Rule of thumb: Use require for preconditions, assert for actual checks.
import (
"testing""github.com/go-openapi/testify/v2/assert""github.com/go-openapi/testify/v2/require")
funcTestFormatted(t*testing.T) {
// Add custom failure message with formattingassert.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")
funcTestForward(t*testing.T) {
a:=assert.New(t)
r:=require.New(t)
// No need to pass 't' each timea.Equal(42, result)
a.NotEmpty(list)
r.NotNil(user)
r.NoError(err)
}
import (
"testing""github.com/go-openapi/testify/v2/assert")
funcTestPanics(t*testing.T) {
// Function should panicassert.Panics(t, func() {
Divide(10, 0)
})
// Function should NOT panicassert.NotPanics(t, func() {
Divide(10, 2)
})
// Function should panic with specific valueassert.PanicsWithValue(t, "division by zero", func() {
Divide(10, 0)
})
}
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")
funcTestBackgroundProcessor(t*testing.T) {
// Simulate a background processor that updates statevarprocessedboolvarmusync.Mutexgofunc() {
time.Sleep(50*time.Millisecond)
mu.Lock()
processed = truemu.Unlock()
}()
// Wait up to 200ms for the background task to complete,// checking every 10msassert.Eventually(t, func() bool {
mu.Lock()
defermu.Unlock()
returnprocessed }, 200*time.Millisecond, 10*time.Millisecond,
"background processor should have completed")
}
// Real-world example: Testing cache warmingfuncTestCacheWarming(t*testing.T) {
cache:=NewCache()
cache.StartWarmup() // Populates cache in background// Verify cache becomes ready within 5 secondsassert.Eventually(t, func() bool {
returncache.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")
funcTestNoDataCorruption(t*testing.T) {
varcounteratomic.Int32stopChan:= make(chanstruct{})
defer close(stopChan)
// Start multiple goroutines incrementing safelyfori:=0; i < 10; i++ {
gofunc() {
ticker:=time.NewTicker(5*time.Millisecond)
deferticker.Stop()
for {
select {
case<-stopChan:
returncase<-ticker.C:
counter.Add(1)
}
}
}()
}
// Verify counter never goes negative (indicating corruption)assert.Never(t, func() bool {
returncounter.Load() < 0 }, 500*time.Millisecond, 20*time.Millisecond,
"counter should never go negative")
}
// Real-world example: Testing rate limiter doesn't exceed thresholdfuncTestRateLimiter(t*testing.T) {
limiter:=NewRateLimiter(100) // 100 requests per second maxstopChan:= make(chanstruct{})
defer close(stopChan)
// Hammer the limiter with requestsfori:=0; i < 50; i++ {
gofunc() {
ticker:=time.NewTicker(1*time.Millisecond)
deferticker.Stop()
for {
select {
case<-stopChan:
returncase<-ticker.C:
limiter.Allow()
}
}
}()
}
// Verify we never exceed the rate limit over 2 secondsassert.Never(t, func() bool {
returnlimiter.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")
funcTestAPIEventualConsistency(t*testing.T) {
// Simulate an eventually-consistent APIapi:=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 tickassert.EventuallyWith(t, func(c*assert.CollectT) {
user, err:=api.GetUser("alice")
// All these assertions must pass togetherassert.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 syncfuncTestDistributedCacheSync(t*testing.T) {
primary:=NewCacheNode("primary")
replica1:=NewCacheNode("replica1")
replica2:=NewCacheNode("replica2")
// Connect nodes for replicationprimary.AddReplica(replica1)
primary.AddReplica(replica2)
// Write to primaryprimary.Set("key", "value", 5*time.Minute)
// Verify value propagates to all replicas with correct TTLassert.EventuallyWith(t, func(c*assert.CollectT) {
val1, ttl1, ok1:=replica1.Get("key")
val2, ttl2, ok2:=replica2.Get("key")
// All replicas must have the valueassert.True(c, ok1, "replica1 should have the key")
assert.True(c, ok2, "replica2 should have the key")
// Values must matchassert.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 fastfuncTestEventuallyWithRequire(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 assertionsassert.NoError(c, err, "health check should not error")
iferr!=nil {
return// Skip remaining checks this tick }
// Now safe to check response fieldsassert.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:
Choose appropriate timeouts: long enough for async operations, short enough for fast test feedback
Choose appropriate tick intervals: frequent enough to catch state changes, infrequent enough to avoid overhead
Use EventuallyWith when you need to understand which assertion failed
Use Eventually for simple boolean conditions (faster, simpler)
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")
funcTestWorkerPool(t*testing.T) {
assert.NoGoRoutineLeak(t, func() {
pool:=NewWorkerPool(10)
pool.Start()
// Submit workpool.Submit(func() { /* do something */ })
// Cleanup MUST happen inside the tested functionpool.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")
funcTestHTTPServer(t*testing.T) {
assert.NoGoRoutineLeak(t, func() {
// Start serverserver:=&http.Server{Addr: ":0", Handler: myHandler}
goserver.ListenAndServe()
// Do some requests...resp, _:=http.Get("http://"+server.Addr+"/health")
resp.Body.Close()
// Shutdown MUST happen inside the tested functionserver.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 checkfuncTestWrong(t*testing.T) {
varserver*Servert.Cleanup(func() { server.Stop() }) // Too late!assert.NoGoRoutineLeak(t, func() {
server = StartServer()
// Leak detected because server is still running })
}
// ✅ CORRECT: cleanup inside tested functionfuncTestCorrect(t*testing.T) {
assert.NoGoRoutineLeak(t, func() {
server:=StartServer()
deferserver.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].typeAssertionsstruct {
*assert.Assertions }
funcNew(tassert.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, prefixstring, msgAndArgs...any) bool {
ifh, ok:=a.T.(assert.H); ok {
h.Helper() // preserve the original failing location }
if !strings.HasPrefix(str, prefix) {
returna.Fail(fmt.Sprintf("Expected %q to start with %q", str, prefix), msgAndArgs...)
}
returntrue }
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")
funcTestExample(t*testing.T) {
assert.Equal(t, "expected", "actual") // Failure will be colorized}
Activation
Colors are activated via command line flag or environment variable:
# Via flaggo test -v -testify.colorized ./...
# Via environment variableTESTIFY_COLORIZED=true go test -v ./...
Themes
Two themes are available for different terminal backgrounds:
# Dark theme (default) - bright colors for dark terminalsgo test -v -testify.colorized ./...
# Light theme - normal colors for light terminalsgo test -v -testify.colorized -testify.theme=light ./...
# Or via environmentTESTIFY_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).
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
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:
Focused - Tests one logical concept
Independent - Can run in any order, in parallel
Repeatable - Same input always produces same result
Fast - Runs quickly to encourage frequent execution
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")
funcTestUserCreation(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 beforeassert.True(t, user.Active)
}
tip
Adopt a test layout similar to your functionality.
# ❌ Don't do this - confusingboolean.go
file.go
all_test.go
// ✅ Better - clear mapping between features and testsboolean.goboolean_test.gofile.gofile_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 maintainfuncTestUserCreation(t*testing.T) {
user:=CreateUser("alice@example.com")
ifassert.NotNil(t, user) {
assert.Equal(t, "alice@example.com", user.Email) // if user is nil, will skip this testassert.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 subcasesfuncTestUserCreation(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 beforeassert.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 maintainfuncTestAdd_PositiveNumbers(t*testing.T) {
result:=Add(2, 3)
assert.Equal(t, 5, result)
}
funcTestAdd_NegativeNumbers(t*testing.T) {
result:=Add(-2, -3)
assert.Equal(t, -5, result)
}
funcTestAdd_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 placefuncTestAdd(t*testing.T) {
// All test cases defined once// Test logic written once// Easy to add new casesforc:=rangeaddTestCases() {
t.Run(c.name, func(t*testing.T) {
t.Parallel()
result:=Add(c.a, c.b)
assert.Equal(t, c.expected, result)
})
}
}
funcaddTestCases() iter.Seq[addTestCase] {
...}
When extracting common assertions into helper functions, use t.Helper() to get better error messages:
import (
"testing""github.com/go-openapi/testify/v2/assert")
funcassertUserValid(t*testing.T, user*User) {
t.Helper() // Makes test failures point to the callerassert.NotNil(t, user)
assert.NotEmpty(t, user.Name)
assert.NotEmpty(t, user.Email)
assert.Greater(t, user.Age, 0)
}
funcTestUserCreation(t*testing.T) {
user:=CreateUser("alice@example.com")
// If this fails, error points HERE, not inside assertUserValidassertUserValid(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")
funcTestAdd(t*testing.T) {
t.Parallel() // Outer test runs in parallelforc:=rangeaddTestCases() {
t.Run(c.name, func(t*testing.T) {
t.Parallel() // Each subtest runs in parallelresult:=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:
funcTestDatabaseOperations(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 failsloaded, err:=db.Find(user.ID)
require.NoError(t, err)
assert.Equal(t, "Alice", loaded.Name)
}
API Reference - Complete assertion catalog organized by domain
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")
funcTestUser(t*testing.T) {
expected:=42actual:=getUserAge()
// Compiles, but type errors appear at runtimeassert.Equal(t, expected, actual)
}
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 codetypeUserIDintvaruserIDs []UserIDassert.ElementsMatch(t, userIDs, getActiveUsers())
// Later: UserID changes to stringtypeUserIDstringvaruserIDs []UserID// Test still compiles!// Fails mysteriously at runtime or passes with wrong comparisonassert.ElementsMatch(t, userIDs, getActiveUsers())
With Generics ✅
// Original codetypeUserIDintvaruserIDs []UserIDassert.ElementsMatchT(t, userIDs, getActiveUsers())
// Later: UserID changes to stringtypeUserIDstringvaruserIDs []UserID// Compiler immediately flags the errorassert.ElementsMatchT(t, userIDs, getActiveUsers())
// ❌ Compile error: type mismatch!
Preventing Wrong Comparisons
Generic assertions force you to think about what you’re comparing:
import (
"testing""github.com/go-openapi/testify/v2/assert")
funcTestPhysicsCalculation(t*testing.T) {
result:=calculateVelocity(mass, force)
expected:=42.0// Type-safe float comparison with deltaassert.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")
funcTestGetUser(t*testing.T) {
result:=getUser(123)
// Need to create a dummy User instanceassert.IsType(t, User{}, result)
// Or use a pointer dummyassert.IsType(t, (*User)(nil), result)
}
New Way (Generic)
import (
"testing""github.com/go-openapi/testify/v2/assert")
funcTestGetUser(t*testing.T) {
result:=getUser(123)
// No dummy value needed!assert.IsOfTypeT[User](t, result)
// For pointer typesassert.IsOfTypeT[*User](t, result)
}
Start with the most common assertions that benefit most from type safety:
// High value: Collection operations (also get big performance wins)assert.Equal→assert.EqualTassert.ElementsMatch→assert.ElementsMatchTassert.Contains→assert.ContainsT (SliceContainsT/MapContainsT/StringContainsT)
// High value: Comparisons (eliminate allocations)assert.Greater→assert.GreaterTassert.Less→assert.LessTassert.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 assertionsgrep -r "assert\.Equal(" . --include="*_test.go"# Find all require.Greater assertionsgrep -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 codeassert.EqualT(t, int64(result), count)
// ❌ Error: mismatched types int64 and int
Fix Option 1: Same Type
// Convert to same typeassert.EqualT(t, int64(result), int64(count))
Fix Option 2: Use Reflection
// If cross-type comparison is intentionalassert.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")
funcTestMixedAssertions(t*testing.T) {
// Use generic where types are knownassert.EqualT(t, 42, getAge())
assert.GreaterT(t, count, 0)
// Keep reflection for dynamic typesvarresultinterface{} = getResult()
assert.Equal(t, expected, result)
// Both styles coexist peacefully}
Performance Benefits
Generic assertions provide significant performance improvements, especially for collection operations:
Operation
Speedup
When It Matters
ElementsMatchT
21-81x faster
Large collections, hot test paths
EqualT
10-13x faster
Most common assertion
GreaterT/LessT
10-22x faster
Numeric comparisons
SliceContainsT
16x faster
Collection membership tests
Learn More
See the complete Performance Benchmarks for detailed analysis and real benchmark results.
Best Practices
✅ Do
Prefer generic variants by default - Type safety is always valuable
assert.EqualT(t, expected, actual) // ✓ Type safe
Let the compiler guide you - Type errors reveal design issues
Leverage performance wins in hot paths - Generic assertions are faster
// Table-driven tests with many iterationsfor_, tc:=rangetestCases {
assert.EqualT(t, tc.expected, tc.actual) // ✓ Fast }
❌ Don’t
Don’t force generics for dynamic types
varresultinterface{} = getResult()
assert.Equal(t, expected, result) // ✓ Reflection is fine here
Don’t use reflection to avoid fixing types
// Bad: Using reflection to bypass type safetyassert.Equal(t, expected, actual) // ✗ Defeats the purpose// Good: Fix the types or use EqualValues if intentionalassert.EqualT(t, expected, actual) // ✓ Type safe
Don’t create unnecessary type conversions
// Bad: Unnecessary conversionassert.EqualT(t, int64(42), int64(result))
// Good: Work with natural typesassert.EqualT(t, 42, result)
Type Constraints Reference
Generic assertions use custom type constraints defined in internal/assertions/generics.go:
Constraint
Definition
Description
Example Types
comparable
Go built-in
Types that support == and !=
int, string, bool, pointers, structs (if all fields are comparable)
Boolean
~bool
Boolean and named bool types
bool, type MyBool bool
Text
~string | ~[]byte
String or byte slice types
string, []byte, custom string/byte types
Ordered
cmp.Ordered | []byte | time.Time
Extendscmp.Ordered with byte slices and time
Standard ordered types plus []byte and time.Time
SignedNumeric
~int... | ~float32 | ~float64
Signed integers and floats
int, int8-int64, float32, float64
UnsignedNumeric
~uint...
Unsigned integers
uint, uint8-uint64
Measurable
SignedNumeric | UnsignedNumeric
All numeric types (for delta comparisons)
Used by InDeltaT/InEpsilonT - supports integers AND floats
RegExp
Text | *regexp.Regexp
Regex pattern or compiled regexp
string, []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
Consolidated Eventually, Never, and EventuallyWith into single pollCondition function
Context-based polling
Internal refactoring
Reimplemented with context-based approach for better resource management
Unified implementation
Internal refactoring
Single implementation eliminates code duplication and prevents resource leaks
func(context.Context) error conditions
extensions to the async domain
control over context allows for more complex cases to be supported
Type parameter
Internal refactoring
Eventually 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
Change
Origin
Description
Renaming
Internal refactorting
EventuallyWithT renamed into EventuallyWith (conflicted with the convention adopted for generics)
Removal
API simplification
Comparison type is removed as a mere alias to func() bool
Equality
Generics
New Generic Functions (4)
Function
Type Parameters
Description
EqualT[V comparable]
Comparable type
Type-safe equality check
NotEqualT[V comparable]
Comparable type
Type-safe inequality check
SameT[V comparable]
Comparable type
Type-safe pointer identity check
NotSameT[V comparable]
Comparable type
Type-safe pointer difference check
Origin: Generics initiative
Performance: 10-13x faster for Equal/NotEqual, 1.5-2x for Same/NotSame
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.
Comprehensive refactor with generic type parameters
ℹ️ Draft for v2.0.0 upstream - We took a different approach with the same objective
Summary Statistics
Category
Count
Implemented/Merged
23
Superseded
4
Monitoring
2
Informational
2
Total Processed
31
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
Changes from v1 - Complete list of all implemented changes and new features
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 separatelymigrate-testify --migrate .
migrate-testify --upgrade-generics .
Pass 1: Import Migration (--migrate)
Rewrites stretchr/testify imports to go-openapi/testify/v2:
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.
Use go additional test flags or environment variables: TESTIFY_COLORIZED=true, TESTIFY_THEME=dark|light
Example:
go test -v -testify.colorized -testify.theme=light .
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:
// Equalityassert.Equal→assert.EqualTassert.NotEqual→assert.NotEqualT// Comparisonsassert.Greater→assert.GreaterTassert.Less→assert.LessTassert.Positive→assert.PositiveTassert.Negative→assert.NegativeT// Collectionsassert.Contains→assert.ContainsT (orSliceContainsT/MapContainsT/StringContainsT)
assert.ElementsMatch→assert.ElementsMatchTassert.Subset→assert.SubsetT// Orderingassert.IsIncreasing→assert.IsIncreasingTassert.IsDecreasing→assert.IsDecreasingT// Type checksassert.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:
The compiler will now catch type errors. This is a feature—it reveals bugs:
// Compiler catches thisassert.EqualT(t, int64(42), int32(42))
// Error: mismatched types int64 and int32// Fix: Use same typeassert.EqualT(t, int64(42), int64(actual))
// Or: Use reflection if cross-type comparison is intentionalassert.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
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"funcTestNoLeak(t*testing.T) {
defergoleak.VerifyNone(t)
// ... test code ...}
// After (with testify v2)import"github.com/go-openapi/testify/v2/assert"funcTestNoLeak(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.
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
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:
Examples - Practical code examples for using testify v2
Usage Guide - API conventions and navigation guide
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.
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.
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.
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.
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.
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: 2min-occurrences: 3cyclop:
max-complexity: 20# the default is too low for most of our functions. 20 is a nicer trade-offgocyclo:
min-complexity: 20exhaustive: # when using default in switch, this should be good enoughdefault-signifies-exhaustive: truedefault-case-required: truelll:
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.
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.
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.
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.
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)
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.
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.
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]”).
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)
Generic assertions (with type parameters): 42 functions
Non-generic assertions (with t T parameter, no type parameters): 82 functions
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
Code generation - Detailed view of our code and doc generator
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:
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"funcStartsWith(tT, str, prefixstring, msgAndArgs...any) bool {
ifh, ok:=t.(H); ok {
h.Helper()
}
if !strings.HasPrefix(str, prefix) {
returnFail(t, fmt.Sprintf("Expected %q to start with %q", str, prefix), msgAndArgs...)
}
returntrue}
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)
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).
//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 passgo test ./...
# Coverage remains highgo 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 validgo 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.
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
Layer
Location
Has external deps?
Purpose
Feature module
enable/yaml/, enable/colors/
Yes (own go.mod)
Imports the real library, wires it in via init()
Public stubs
enable/stubs/yaml/, enable/stubs/colors/
No
Stable public API that delegates to internal package
Internal stubs
internal/assertions/enable/yaml/, .../colors/
No
Holds function pointers, panics when unset
Assertions
internal/assertions/*.go
No
Calls 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:
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.
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.
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
The first release comes with zero dependencies and an unstable API (see below our use case)
This project is going to be injected as the main and sole test dependency of the go-openapi libraries
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.
Valuable pending pull requests from the original project could be merged (e.g. JSONEqBytes) or transformed as “enable” modules (e.g. colorized output)
More testing and bug fixes (from upstream or detected during our testing)
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.
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:
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:
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:
packageyoursimport (
"testing""github.com/stretchr/testify/assert")
funcTestSomething(t*testing.T) {
// assert equalityassert.Equal(t, 123, 123, "they should be equal")
// assert inequalityassert.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)ifassert.NotNil(t, object) {
// now we know that object isn't nil, we are safe to make// further assertions without causing any errorsassert.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:
packageyoursimport (
"testing""github.com/stretchr/testify/assert")
funcTestSomething(t*testing.T) {
assert:=assert.New(t)
// assert equalityassert.Equal(123, 123, "they should be equal")
// assert inequalityassert.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)ifassert.NotNil(object) {
// now we know that object isn't nil, we are safe to make// further assertions without causing any errorsassert.Equal("Something", object.Value)
}
}
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.
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:
packageyoursimport (
"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.typeMyMockedObjectstruct {
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(numberint) (bool, error) {
args:=m.Called(number)
returnargs.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.funcTestSomething(t*testing.T) {
// create an instance of our test objecttestObj:= new(MyMockedObject)
// set up expectationstestObj.On("DoSomething", 123).Return(true, nil)
// call the code we are testingtargetFuncThatDoesSomethingWithObj(testObj)
// assert that the expectations were mettestObj.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)funcTestSomethingWithPlaceholder(t*testing.T) {
// create an instance of our test objecttestObj:= new(MyMockedObject)
// set up expectations with a placeholder in the argument listtestObj.On("DoSomething", mock.Anything).Return(true, nil)
// call the code we are testingtargetFuncThatDoesSomethingWithObj(testObj)
// assert that the expectations were mettestObj.AssertExpectations(t)
}
// TestSomethingElse2 is a third example that shows how you can use// the Unset method to cleanup handlers and then add new ones.funcTestSomethingElse2(t*testing.T) {
// create an instance of our test objecttestObj:= new(MyMockedObject)
// set up expectations with a placeholder in the argument listmockCall:=testObj.On("DoSomething", mock.Anything).Return(true, nil)
// call the code we are testingtargetFuncThatDoesSomethingWithObj(testObj)
// assert that the expectations were mettestObj.AssertExpectations(t)
// remove the handler now so we can add another one that takes precedencemockCall.Unset()
// return false now instead of truetestObj.On("DoSomething", mock.Anything).Return(false, nil)
testObj.AssertExpectations(t)
}
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 importsimport (
"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 contexttypeExampleTestSuitestruct {
suite.SuiteVariableThatShouldStartAtFiveint}
// Make sure that VariableThatShouldStartAtFive is set to five// before each testfunc (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.RunfuncTestExampleTestSuite(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
// Basic importsimport (
"testing""github.com/stretchr/testify/suite")
// Define the suite, and absorb the built-in basic suite// functionality from testify - including assertion methods.typeExampleTestSuitestruct {
suite.SuiteVariableThatShouldStartAtFiveint}
// Make sure that VariableThatShouldStartAtFive is set to five// before each testfunc (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.RunfuncTestExampleTestSuite(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:
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.
Motivation
From the maintainers of testify, it looks like a v2 will eventually be released, but they’ll do it at their own pace.
However, at go-openapi we would like to address the well-known issues in testify with different priorities.
With this fork, we want to:
remove all external dependencies.
make it easy to maintain and extend.
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.
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).
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.
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.
We want to add new features like generics, more useful assertions for JSON and safety checks.
We want to get rid of the API quirks and gotchas that panic or return unexpected results.
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.
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.
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)@TestpublicvoidtestUserCreation() {
User user = createUser("alice@example.com");
assertNotNull(user);
assertEquals("alice@example.com", user.getEmail());
}
# pytest (Python)deftest_user_creation():
user = create_user("alice@example.com")
assert user isnotNoneassert user.email =="alice@example.com"
// NUnit (C#)[Test]publicvoid TestUserCreation()
{
var user = CreateUser("alice@example.com");
Assert.IsNotNull(user);
Assert.AreEqual("alice@example.com", user.Email);
}
# 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"
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")
funcTestAdd(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:
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")
funcTestUserAPI(t*testing.T) {
t.Run("creation", func(t*testing.T) { // Standard Go subtestt.Parallel() // Standard Go test parallelismuser:=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:
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:
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?