Skip to content
gohacks.dev
Testing

A Testify Cheat Sheet for Assertions Mocks and Suites

A testify cheat sheet for Go: the assertions worth knowing, assert against require, mock calls, suite hooks and HTTP helpers, with the one trap each has.

5 min read
Summarize in AI
Flat illustration of a test report with one failing test.

A compact reference for the testify functions that cover most Go tests. Everything on the sheet ran in one test on Go 1.25.6 with testify v1.11.1. Argument order is (t, expected, actual) unless noted, and every assert function has a require twin.

Tested with Go 1.25.6, testify v1.11.1, macOS on Apple Silicon (arm64)

cs_test.goGo
package main

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestCheatSheet(t *testing.T) {
	a := assert.New(t)
	a.Equal(2, 1+1) // equal, with ObjectsAreEqual semantics
	a.NotEqual(1, 2)
	a.True(true)
	a.Nil((*int)(nil)) // typed nil pointers count as nil
	a.NotNil(1)
	a.Zero(0)
	a.Empty("")
	a.Len([]int{1, 2}, 2)
	a.Contains("hello", "ell")
	a.Subset([]int{1, 2, 3}, []int{2})
	a.ElementsMatch([]int{1, 2}, []int{2, 1})
	a.Greater(3, 2)
	a.GreaterOrEqual(3, 3)
	a.InDelta(1.0, 1.0004, 0.001)
	a.WithinDuration(time.Unix(0, 0), time.Unix(1, 0), 2*time.Second)
	a.Regexp(`^h.+o$`, "hello")
	a.JSONEq(`{"a":1,"b":2}`, `{"b":2,"a":1}`)
}

func TestSameVsEqual(t *testing.T) {
	x := &struct{ N int }{1}
	y := &struct{ N int }{1}
	assert.Equal(t, x, y) // pointers compared by ObjectsAreEqual: contents
	require.NotSame(t, x, y)
	t.Log("Equal compares contents, Same compares identity")
}

func main() {}
Shell
go test -count=1 -v . 2>&1 | grep -vE '^(=== RUN|=== PAUSE|=== CONT|=== NAME|PASS|ok|FAIL$|FAIL\s|exit status)' | sed -E 's/ \([0-9.]+s\)//; s#^ +[a-z_0-9]+\.go:[0-9]+: #    #; s/\x1b\[[0-9;]*m//g; s#/[^[:space:]]*/([a-z_0-9]+\.go)#\1#g; s/[[:space:]]+$//' | grep -vE '^\s*$'
Text
--- PASS: TestCheatSheet
    Equal compares contents, Same compares identity
--- PASS: TestSameVsEqual

Assertions by job

JobFunctionComparesWatch out for
ValuesEqual, NotEqualDeep equality, same typeEqual(t, 1, int64(1)) fails
ValuesEqualValuesAfter conversionHides real type bugs
ValuesExactlySame type and valueStricter than Equal for numbers
IdentitySame, NotSamePointer identityEqual on pointers compares contents
BooleansTrue, FalseA boolPrints nothing useful on failure
NilNil, NotNilNil, including typed nilsA nil pointer in an interface counts as nil
ZeroZero, NotZeroThe type's zero value
EmptyEmpty, NotEmptyZero, or length 0Works on slices, maps, strings, channels
ErrorsNoError, ErrorPresenceDoes not say which error
ErrorsErrorIs, ErrorAsThe error chainPass a pointer to ErrorAs
ErrorsEqualError, ErrorContainsThe message textCouples the test to wording
SizeLenLength of slice, map, string, channel
CollectionsContains, NotContainsSubstring, element, map key
CollectionsSubset, ElementsMatchMembership, ignoring orderElementsMatch is the one for unordered results
OrderGreater, GreaterOrEqual, Less, LessOrEqualOrdered valuesSame types required
NumbersInDelta, InEpsilonWithin an absolute or relative errorNever Equal for floats
TimeWithinDurationTwo times within a deltaInject a clock instead where possible
TextRegexp, NotRegexpA pattern against a string
StructuredJSONEq, YAMLEqParsed documentsKey order and whitespace ignored
PanicsPanics, PanicsWithValue, PanicsWithError, NotPanicsA function's panicTakes a func()
Time passingEventually, Never, EventuallyWithTPolls a conditionCondition runs on another goroutine
FilesFileExists, DirExists, NoFileExistsPaths

The one test that ran them

All eighteen checks in TestCheatSheet passed. Two behaviours are worth stating because they trip people.

assert.Nil((*int)(nil)) passed. A typed nil pointer is treated as nil by testify's isNil, which checks the value's kind and calls IsNil; a plain == nil comparison on an interface holding a typed nil would be false. That is convenient and slightly different from the language rule, so be aware of it when an error interface holds a typed nil.

TestSameVsEqual compares two distinct pointers to structs with equal contents. assert.Equal passes, because ObjectsAreEqual looks at what they point to, and require.NotSame passes, because the addresses differ. The log line Equal compares contents, Same compares identity is the summary.

Assert and require

assertrequire
On failureRecords it, test continuesRecords it, stops the test
Mechanismt.Errorft.FailNow
Use forIndependent checks on a resultGuards: errors, nil, length
From a goroutineSafeNot safe: stops only that goroutine

The long form is in assert versus require.

Mocks

TaskCode
Declaretype M struct{ mock.Mock }, methods call m.Called(args...)
Expectm.On("Method", arg1, arg2).Return(v1, v2)
Limit.Once(), .Twice(), .Times(n), .Maybe()
Matchmock.Anything, mock.AnythingOfType("T"), mock.MatchedBy(func(x T) bool {...})
Side effect.Run(func(args mock.Arguments){...})
Verifym.AssertExpectations(t), AssertCalled, AssertNotCalled, AssertNumberOfCalls
Read returnargs.Error(0), args.String(0), args.Int(0), args.Bool(0), args.Get(0)

The trap: an unexpected call panics, a missing call is only found by AssertExpectations. Details in the mock article.

Suites

HookRuns
SetupSuite, TearDownSuiteOnce per suite
SetupTest, TearDownTestAround every test method
SetupSubTest, TearDownSubTestAround every s.Run subtest
BeforeTest, AfterTestAround each method, with names

Methods run alphabetically. Start with suite.Run(t, new(S)). See the suite article.

HTTP helpers

assert.HTTPSuccess, HTTPError, HTTPRedirect, HTTPStatusCode, HTTPBodyContains, HTTPBodyNotContains take a handler function, method, URL and url.Values, and call the handler once per assertion. For headers or JSON bodies use httptest; see the HTTP article.

The traps, in one place

  • Equal needs matching types: an int and an int64 never compare equal.
  • Equal on floats is a coin toss; use InDelta.
  • Contains on a map checks keys, not values.
  • Empty is true for zero values as well as empty collections, so Empty(t, 0) passes.
  • ElementsMatch compares as multisets, so duplicates count.
  • Eventually runs its condition on another goroutine.
  • assert.New(t) binds the t you give it; create it inside the subtest.
  • AssertExpectations must be called, or a mock's missing calls go unnoticed.
  • mock.Anything weakens the check to the point of "was it called".
  • suite methods run in alphabetical order, and fields persist between them.

Habits

  1. Expected first, actual second.
  2. require for the line that makes the next line safe.
  3. Name the case in loops: assert.Equal(t, want, got, "case %q", name).
  4. Create assert.New(t) inside the subtest.
  5. Prefer ErrorIs and Len to Error and True.
  6. Verify mocks every time, because a mock that is never verified cannot fail for a call that never happened.

The release notes live in the testify repository, and the reference is the testify documentation on pkg.go.dev, and the tutorial that explains each family is the assertions article.

Share

Keep reading

Get new articles by email

We send one email when a new article is published. No spam.

Unsubscribe in one click. No spam, ever.