Testify Assert vs Require and When Each One Fits Best
Choose between testify assert and require: assert continues after a failure and shows every mismatch, require stops the test. Rules and a nil guard example.

Testify has two packages that look the same. assert.Equal(t, want, got) and require.Equal(t, want, got) compare the same values and print the same message. The one difference is what happens next: assert lets the test continue, and require ends it. That difference decides how useful the failure output is, and choosing wrongly produces either noise or hidden failures.
Tested with Go 1.25.6, testify v1.11.1, macOS on Apple Silicon (arm64)
The behaviour in one run
package main
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAssertKeepsGoing(t *testing.T) {
assert.Equal(t, 1, 2, "first")
t.Log("assert: this line still runs")
assert.Equal(t, 3, 4, "second")
}
func TestRequireStops(t *testing.T) {
require.Equal(t, 1, 2, "first")
t.Log("require: this line never runs")
require.Equal(t, 3, 4, "second")
}
func TestRequireGuardsNilDeref(t *testing.T) {
var p *struct{ N int }
require.NotNil(t, p) // without this, p.N below panics
t.Log(p.N)
}
func main() {}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*$' Error Trace: ar_test.go:11
Error: Not equal:
expected: 1
actual : 2
Test: TestAssertKeepsGoing
Messages: first
assert: this line still runs
Error Trace: ar_test.go:13
Error: Not equal:
expected: 3
actual : 4
Test: TestAssertKeepsGoing
Messages: second
--- FAIL: TestAssertKeepsGoing
Error Trace: ar_test.go:17
Error: Not equal:
expected: 1
actual : 2
Test: TestRequireStops
Messages: first
--- FAIL: TestRequireStops
Error Trace: ar_test.go:24
Error: Expected value not to be nil.
Test: TestRequireGuardsNilDeref
--- FAIL: TestRequireGuardsNilDerefassert keeps going. TestAssertKeepsGoing has two failing comparisons with a log line between them. The output shows the first failure (expected: 1, actual: 2, message first), then the log line assert: this line still runs, then the second failure (expected: 3, actual: 4, message second). Both mismatches appear in one run. That is the benefit: you fix two things per cycle instead of one.
require stops. TestRequireStops has the same two comparisons. The output shows the first failure and then the end of the test. The log line and the second comparison never ran. require calls t.FailNow, which ends the goroutine the test is running in, exactly like t.Fatal.
require protects what follows. TestRequireGuardsNilDeref checks require.NotNil(t, p) and then reads p.N. The test fails at the check with Expected value not to be nil., and the dereference never happens. With assert.NotNil the test would have continued to p.N and panicked with invalid memory address or nil pointer dereference, and the panic would have buried the one line that explained the cause.
The rule
Ask of each check: if this fails, is the next line still meaningful?
- No: use
require. The common cases are an error check before using the returned value, a nil or length check before indexing or dereferencing, a setup step in a test where nothing works without it, and a check of a precondition. - Yes: use
assert. The common cases are comparing several fields of one result, checking several outputs of one call, and any check where seeing all the mismatches helps.
A typical test uses both: require.NoError(t, err) after the call, then a series of assert calls on the fields of the result. The first protects against a nil result and the rest report everything.
got, err := Parse(input)
require.NoError(t, err)
assert.Equal(t, "ada", got.Name)
assert.Equal(t, 36, got.Age)
assert.Len(t, got.Roles, 2)Only the NoError stops the test. If Name and Age are both wrong, one run reports both.
Common mistakes
Everything is require. A test that stops at the first mismatch shows one failure at a time. In a table test with forty rows it is worse: stopping ends the subtest, not the whole table, so it is acceptable inside t.Run, but within one case it hides information. Use require for guards only.
Everything is assert. The test continues into code that assumes a good state. The typical symptoms are a nil pointer panic after an ignored error, an index out of range after an unchecked length, and a cascade of ten failures that all come from the first one. When a failure log is long, the first line is the cause; if it was an assert that should have been require, the cascade is noise.
require in a goroutine. FailNow stops only the calling goroutine and must run in the test's own. In another goroutine it marks the test failed and ends that goroutine, while the test goes on. go vet reports call to (*testing.T).FailNow from a non-test goroutine for direct calls, and the same problem applies to the testify wrapper. Collect errors with a channel or errgroup and check them in the test goroutine.
Mixing them without noticing. A file that imports both packages and uses assert and require inconsistently is hard to read. Establish the convention: require for guards and setup, assert for results.
Using assert return values for flow. if !assert.NoError(t, err) { return } is equivalent to require.NoError(t, err) except that it ends the function, not the goroutine. It is occasionally right inside a helper; elsewhere write require.
Cleanup and FailNow
FailNow runs deferred functions and t.Cleanup callbacks before the test ends, so resources are still released after a require failure. That makes require safe for guards that sit after setup: the temporary directory is removed, the server closed. What does not run is the rest of the test body.
Subtests behave as you would hope: require inside t.Run ends the subtest and the parent continues with the next one. A require in the parent ends the parent and skips later subtests that have not started.
The suite and New forms
In a suite, s.Require() and s.Assert() return the two families bound to the suite's T, and s.Equal is assert. With assert.New(t) and require.New(t) you get objects with the first argument dropped: req := require.New(t), req.NoError(err). The same rule of choosing applies. A short habit: bind both near the top of the test, is := assert.New(t) and must := require.New(t), and the call sites read as must.NoError(err) and is.Equal(...), which makes the severity of each check visible.
A worked decision
Take a handler test. It sends a request, decodes the body and checks the fields. The steps and the right choice for each:
req, err := http.NewRequest(...)thenrequire.NoError(t, err). Without a request nothing else can run.resp, err := client.Do(req)thenrequire.NoError(t, err). The next lines readresp.StatusCode; a nilrespwould panic.defer resp.Body.Close(). Not an assertion.assert.Equal(t, 200, resp.StatusCode). Independent of the body check, and the body can still tell us why the status is wrong.var got Order; require.NoError(t, json.NewDecoder(resp.Body).Decode(&got)). A decode failure leavesgothalf empty, so the field checks would report misleading mismatches.assert.Equal(t, "o-1", got.ID),assert.Equal(t, 3, len(got.Lines)),assert.Equal(t, "paid", got.Status). Three independent checks of one result: if two are wrong, you want both.
Two require calls at the places where the world stops making sense, assert everywhere else. The same split works for almost any test, and it is easy to teach: guards are require, claims are assert.
Linting the choice
testifylint, a golangci-lint plugin, checks several related mistakes: using assert for an error followed by a dereference, reversed argument order, assert.True(t, a == b) instead of Equal, and require inside goroutines. It is described here, not run. If your team adopts testify, turning that linter on keeps the convention without a review comment. The related guidance on t.Fatal and goroutines is in the mistakes roundup.
Which one is safer as a default
If you must choose one default for a team, require is the safer: a test that stops early is easy to understand and cannot cascade into misleading panics. assert is the more informative, and is best used deliberately where several independent checks sit together. A code base that defaults to assert accumulates tests that panic on nil dereferences; one that defaults to require accumulates tests that show one failure at a time. The first is the more expensive habit. The documentation for both is at pkg.go.dev/github.com/stretchr/testify/require, and the underlying testing methods are in the testing.T reference. For the assertion vocabulary, see the tutorial, and for patterns in tables, testify table tests.


