// 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")
}