// real-world test would inject *testing.T from TestBlocked(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 TestBlocked(t *testing.T)success:=assert.Blocked(t, make(chanstruct{}))
fmt.Printf("success: %t\n", success)
}
// real-world test would inject *testing.T from TestBlocked(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 TestBlocked(t *testing.T)require.Blocked(t, make(chanstruct{}))
fmt.Println("passed")
}
// real-world test would inject *testing.T from TestBlockedT(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 TestBlockedT(t *testing.T)success:=assert.BlockedT(t, make(chanstruct{}))
fmt.Printf("success: %t\n", success)
}
// real-world test would inject *testing.T from TestBlockedT(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 TestBlockedT(t *testing.T)require.BlockedT(t, make(chanstruct{}))
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.
Panic recovery
A panicking condition is treated as an error, causing Consistently to fail immediately.
See Eventually for details.
Wrap the condition with WithSynctest (or WithSynctestContext) to run
the polling loop inside a [testing/synctest] bubble, which uses a fake
clock. This eliminates timing-induced flakiness and makes the tick count
deterministic. See WithSynctest for the constraints (no real I/O in
the condition, requires [*testing.T]).
Examples
assertions.Consistently(t, func() bool { returntrue }, time.Second, 10*time.Millisecond)
Seealso [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) for details about using context, concurrency, and panic recovery.success: func() bool { returntrue }, 100*time.Millisecond, 20*time.Millisecondfailure: func() bool { returnfalse }, 100*time.Millisecond, 20*time.Millisecond
// 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""sync/atomic""testing""time""github.com/go-openapi/testify/v2/assert")
funcmain() {
t:= new(testing.T) // normally provided by test// An invariant that must hold throughout the observation period.varcounteratomic.Int32counter.Store(5)
invariant:=func() bool { returncounter.Load() < 10 }
result:=assert.Consistently(t, assert.WithSynctest(invariant), 1*time.Hour, 1*time.Minute)
fmt.Printf("invariant held: %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())
}
// 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// An invariant that must hold throughout the observation period.varcounteratomic.Int32counter.Store(5)
invariant:=func() bool { returncounter.Load() < 10 }
require.Consistently(t, require.WithSynctest(invariant), 1*time.Hour, 1*time.Minute)
fmt.Printf("invariant held: %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.
Panic recovery
If the condition panics, the panic is recovered and treated as a failed tick
(equivalent to returning false or a non-nil error). For Eventually, this means
the poller retries on the next tick — if a later tick succeeds, the assertion
succeeds. For Never and Consistently, a panic is treated as the condition
erroring, which causes immediate failure.
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).
Synctest (opt-in)
Wrap the condition with WithSynctest (or WithSynctestContext) to run
the polling loop inside a [testing/synctest] bubble, which uses a fake
clock. This eliminates timing-induced flakiness and makes the tick count
deterministic. See WithSynctest for the constraints (no real I/O in
the condition, requires *testing.T).
// 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""sync/atomic""testing""time""github.com/go-openapi/testify/v2/assert")
funcmain() {
t:= new(testing.T) // normally provided by test// A counter that converges on the 5th poll — no external time pressure.varattemptsatomic.Int32cond:=func() bool {
returnattempts.Add(1) ==5 }
// 1-hour/1-minute: under fake time this is instantaneous and// deterministic — exactly 5 calls to the condition.result:=assert.Eventually(t, assert.WithSynctest(cond), 1*time.Hour, 1*time.Minute)
fmt.Printf("ready: %t, attempts: %d", result, attempts.Load())
}
// 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())
}
// 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// A counter that converges on the 5th poll — no external time pressure.varattemptsatomic.Int32cond:=func() bool {
returnattempts.Add(1) ==5 }
// 1-hour/1-minute: under fake time this is instantaneous and// deterministic — exactly 5 calls to the condition.require.Eventually(t, require.WithSynctest(cond), 1*time.Hour, 1*time.Minute)
fmt.Printf("ready: %t, attempts: %d", !t.Failed(), attempts.Load())
}
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 (directly, or transitively through require assertions)
fails the current tick only: the poller will retry on the next tick. This means
require-style assertions inside EventuallyWith behave naturally — they abort
the current evaluation and let the polling loop converge.
To abort the whole assertion immediately (e.g. when the condition can no longer
be expected to succeed), call CollectT.Cancel.
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.
The condition is wrapped in its own goroutine, so a call to runtime.Goexit
(e.g. via require assertions or CollectT.FailNow) cleanly aborts only the
current tick.
Panic recovery
If the condition panics, the panic is recovered and recorded as an error in the
CollectT for that tick. The poller treats it as a failed tick and retries on the
next one. If the assertion times out, the panic error is included in the collected
errors reported on the parent t.
See Eventually for the general panic recovery semantics.
Synctest (opt-in)
Wrap the condition with WithSynctestCollect (or WithSynctestCollectContext)
to run the polling loop inside a [testing/synctest] bubble, which uses
a fake clock. This eliminates timing-induced flakiness and makes the
tick count deterministic. See WithSynctest for the constraints (no
real I/O in the condition, requires [*testing.T]).
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.Millisecondfailure: func(c*CollectT) { c.Cancel() }, 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")
}
Wrap the condition with WithSynctest to run the polling loop inside a
[testing/synctest] bubble, which uses a fake clock. This eliminates
timing-induced flakiness and makes the tick count deterministic. See
WithSynctest for the constraints (no real I/O in the condition,
requires [*testing.T]). Note: Never does not accept the context/error
form of condition, so WithSynctestContext does not apply here.
Examples
assertions.Never(t, func() bool { returnfalse }, time.Second, 10*time.Millisecond)
Seealso [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) for details about using context, concurrency, and panic recovery.success: func() bool { returnfalse }, 100*time.Millisecond, 20*time.Millisecondfailure: func() bool { returntrue }, 100*time.Millisecond, 20*time.Millisecond
// 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""sync/atomic""testing""time""github.com/go-openapi/testify/v2/assert")
funcmain() {
t:= new(testing.T) // normally provided by test// A flag that should remain false across the whole observation period.varflippedatomic.Boolresult:=assert.Never(t, assert.WithSynctest(flipped.Load), 1*time.Hour, 1*time.Minute)
fmt.Printf("never flipped: %t", result)
}
// 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 TestNever(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 flag that should remain false across the whole observation period.varflippedatomic.Boolrequire.Never(t, require.WithSynctest(flipped.Load), 1*time.Hour, 1*time.Minute)
fmt.Printf("never flipped: %t", !t.Failed())
}
// real-world test would inject *testing.T from TestNotBlocked(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 TestNotBlocked(t *testing.T)require.NotBlocked(t, sendChanMessage())
fmt.Println("passed")
}
funcsendChanMessage() chanstruct{} {
ch:= make(chanstruct{}, 1)
ch<-struct{}{}
returnch}
// real-world test would inject *testing.T from TestNotBlockedT(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 TestNotBlockedT(t *testing.T)require.NotBlockedT(t, sendChanMessage())
fmt.Println("passed")
}
funcsendChanMessage() chanstruct{} {
ch:= make(chanstruct{}, 1)
ch<-struct{}{}
returnch}