📖 23 min read (~ 4900 words).

Condition

Expressing Assertions Using Conditions

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 9 functionalities. Generic assertions are marked with a .

Blocked

Blocked asserts that a channel is blocked on receive.

It always fails if the operand is not a channel, or if the channel is send-only.

Examples
	ch := make(chan struct{})
	assertions.Blocked(t, ch)
	success:  make(chan struct{})
	failure:  sendChanMessage()
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestBlocked(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestBlocked(t *testing.T)
    	success := assert.Blocked(t, make(chan struct{}))
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestBlocked(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestBlocked(t *testing.T)
    	require.Blocked(t, make(chan struct{}))
    	fmt.Println("passed")
    
    }

BlockedT[E any, CHAN ~chan E]

BlockedT asserts that a channel is blocked on receive.

Examples
	ch := make(chan struct{})
	assertions.BlockedT(t, ch)
	success:  make(chan struct{})
	failure:  sendChanMessage()
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestBlockedT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestBlockedT(t *testing.T)
    	success := assert.BlockedT(t, make(chan struct{}))
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestBlockedT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestBlockedT(t *testing.T)
    	require.BlockedT(t, make(chan struct{}))
    	fmt.Println("passed")
    
    }

Condition

Condition uses a comparison function to assert a complex condition.

Examples
	assertions.Condition(t, func() bool { return myCondition })
	success:  func() bool { return true }
	failure:  func() bool { return false }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestCondition(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestCondition(t *testing.T)
    	success := assert.Condition(t, func() bool {
    		return true
    	})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestCondition(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestCondition(t *testing.T)
    	require.Condition(t, func() bool {
    		return true
    	})
    	fmt.Println("passed")
    
    }

Consistently[C Conditioner]

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.

Concurrency

See Eventually.

Attention point

See Eventually.

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]).

Examples
	assertions.Consistently(t, func() bool { return true }, time.Second, 10*time.Millisecond)
See also [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 { return true }, 100*time.Millisecond, 20*time.Millisecond
	failure:  func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestConsistently(t *testing.T)
    	success := assert.Consistently(t, func() bool {
    		return true
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"context"
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate a service that stays healthy.
    	healthCheck := func(_ context.Context) error {
    		return nil // always healthy
    	}
    
    	result := assert.Consistently(t, healthCheck, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("consistently healthy: %t", result)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A counter that stays within bounds during the test.
    	var counter atomic.Int32
    	counter.Store(5)
    
    	result := assert.Consistently(t, func() bool {
    		return counter.Load() < 10
    	}, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("consistently under limit: %t", result)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// An invariant that must hold throughout the observation period.
    	var counter atomic.Int32
    	counter.Store(5)
    	invariant := func() bool { return counter.Load() < 10 }
    
    	result := assert.Consistently(t, assert.WithSynctest(invariant), 1*time.Hour, 1*time.Minute)
    
    	fmt.Printf("invariant held: %t", result)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestConsistently(t *testing.T)
    	require.Consistently(t, func() bool {
    		return true
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Println("passed")
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"context"
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate a service that stays healthy.
    	healthCheck := func(_ context.Context) error {
    		return nil // always healthy
    	}
    
    	require.Consistently(t, healthCheck, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("consistently healthy: %t", !t.Failed())
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A counter that stays within bounds during the test.
    	var counter atomic.Int32
    	counter.Store(5)
    
    	require.Consistently(t, func() bool {
    		return counter.Load() < 10
    	}, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("consistently under limit: %t", !t.Failed())
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestConsistently(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// An invariant that must hold throughout the observation period.
    	var counter atomic.Int32
    	counter.Store(5)
    	invariant := func() bool { return counter.Load() < 10 }
    
    	require.Consistently(t, require.WithSynctest(invariant), 1*time.Hour, 1*time.Minute)
    
    	fmt.Printf("invariant held: %t", !t.Failed())
    
    }

Eventually[C Conditioner]

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”

  • Never (func() bool) : “never returns true”

  • Consistently (func() bool): “always returns true”

  • Eventually (func(ctx) error) : “eventually returns nil”

  • Never (func(ctx) error) : not supported, use Consistently instead (avoids confusion with double negation)

  • Consistently (func(ctx) error): “always returns nil”

Concurrency

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.

The recovered panic is wrapped as an error with the sentinel errConditionPanicked, detectable with errors.Is.

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).

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).

Examples
	assertions.Eventually(t, func() bool { return true }, time.Second, 10*time.Millisecond)
	success:  func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond
	failure:  func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEventually(t *testing.T)
    	success := assert.Eventually(t, func() bool {
    		return true
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate an async operation that completes after a short delay.
    	var ready atomic.Bool
    	go func() {
    		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)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"context"
    	"errors"
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate a service that becomes healthy after a few attempts.
    	var attempts atomic.Int32
    	healthCheck := func(_ context.Context) error {
    		if attempts.Add(1) < 3 {
    			return errors.New("service not ready")
    		}
    
    		return nil
    	}
    
    	result := assert.Eventually(t, healthCheck, 200*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("eventually healthy: %t", result)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"context"
    	"errors"
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	var attempts atomic.Int32
    	healthCheck := func(_ context.Context) error {
    		if attempts.Add(1) < 3 {
    			return errors.New("service not ready")
    		}
    
    		return nil
    	}
    
    	result := assert.Eventually(t, assert.WithSynctestContext(healthCheck), 1*time.Hour, 1*time.Minute)
    
    	fmt.Printf("healthy: %t, attempts: %d", result, attempts.Load())
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A counter that converges on the 5th poll — no external time pressure.
    	var attempts atomic.Int32
    	cond := func() bool {
    		return attempts.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())
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestEventually(t *testing.T)
    	require.Eventually(t, func() bool {
    		return true
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Println("passed")
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate an async operation that completes after a short delay.
    	var ready atomic.Bool
    	go func() {
    		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())
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"context"
    	"errors"
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// Simulate a service that becomes healthy after a few attempts.
    	var attempts atomic.Int32
    	healthCheck := func(_ context.Context) error {
    		if attempts.Add(1) < 3 {
    			return errors.New("service not ready")
    		}
    
    		return nil
    	}
    
    	require.Eventually(t, healthCheck, 200*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("eventually healthy: %t", !t.Failed())
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"context"
    	"errors"
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	var attempts atomic.Int32
    	healthCheck := func(_ context.Context) error {
    		if attempts.Add(1) < 3 {
    			return errors.New("service not ready")
    		}
    
    		return nil
    	}
    
    	require.Eventually(t, require.WithSynctestContext(healthCheck), 1*time.Hour, 1*time.Minute)
    
    	fmt.Printf("healthy: %t, attempts: %d", !t.Failed(), attempts.Load())
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventually(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A counter that converges on the 5th poll — no external time pressure.
    	var attempts atomic.Int32
    	cond := func() bool {
    		return attempts.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[C CollectibleConditioner]

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 := false
	go func() {
		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 tick
		assertions.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.Millisecond
	failure: func(c *CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond
	failure: func(c *CollectT) { c.Cancel() }, 100*time.Millisecond, 20*time.Millisecond
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventuallyWith(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	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)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventuallyWith(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	var attempts atomic.Int32
    	cond := func(c *assert.CollectT) {
    		n := attempts.Add(1)
    		assert.Equal(c, int32(3), n, "not yet converged")
    	}
    
    	result := assert.EventuallyWith(t, assert.WithSynctestCollect(cond), 1*time.Hour, 1*time.Minute)
    
    	fmt.Printf("converged: %t, attempts: %d", result, attempts.Load())
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventuallyWith(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	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")
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestEventuallyWith(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	var attempts atomic.Int32
    	cond := func(c *require.CollectT) {
    		n := attempts.Add(1)
    		require.Equal(c, int32(3), n, "not yet converged")
    	}
    
    	require.EventuallyWith(t, require.WithSynctestCollect(cond), 1*time.Hour, 1*time.Minute)
    
    	fmt.Printf("converged: %t, attempts: %d", !t.Failed(), attempts.Load())
    
    }

Never[C NeverConditioner]

Never asserts that the given condition is never satisfied until timeout, periodically checking the target function at each tick.

Never is the opposite of Eventually (“at least once”). It succeeds if the timeout is reached without the condition ever returning true.

If the parent context is cancelled before the timeout, Never fails.

Alternative condition signature

The simplest form of condition is:

func() bool

Use Consistently instead if you want to use a condition returning an error.

Panic recovery

A panicking condition is treated as an error, causing Never to fail immediately. See Eventually for details.

Concurrency

See Eventually.

Attention point

See Eventually.

Synctest (opt-in)

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 { return false }, time.Second, 10*time.Millisecond)
See also [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 { return false }, 100*time.Millisecond, 20*time.Millisecond
	failure:  func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNever(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNever(t *testing.T)
    	success := assert.Never(t, func() bool {
    		return false
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNever(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A channel that should remain empty during the test.
    	events := make(chan struct{}, 1)
    
    	result := assert.Never(t, func() bool {
    		select {
    		case <-events:
    			return true // event received = condition becomes true = Never fails
    		default:
    			return false
    		}
    	}, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("never received: %t", result)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNever(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A flag that should remain false across the whole observation period.
    	var flipped atomic.Bool
    	result := assert.Never(t, assert.WithSynctest(flipped.Load), 1*time.Hour, 1*time.Minute)
    
    	fmt.Printf("never flipped: %t", result)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNever(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNever(t *testing.T)
    	require.Never(t, func() bool {
    		return false
    	}, 100*time.Millisecond, 20*time.Millisecond)
    	fmt.Println("passed")
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNever(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A channel that should remain empty during the test.
    	events := make(chan struct{}, 1)
    
    	require.Never(t, func() bool {
    		select {
    		case <-events:
    			return true // event received = condition becomes true = Never fails
    		default:
    			return false
    		}
    	}, 100*time.Millisecond, 10*time.Millisecond)
    
    	fmt.Printf("never received: %t", !t.Failed())
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNever(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync/atomic"
    	"testing"
    	"time"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	// A flag that should remain false across the whole observation period.
    	var flipped atomic.Bool
    	require.Never(t, require.WithSynctest(flipped.Load), 1*time.Hour, 1*time.Minute)
    
    	fmt.Printf("never flipped: %t", !t.Failed())
    
    }

NotBlocked

NotBlocked asserts that a channel is not blocked on receive.

It always fails if the operand is not a channel, or if the channel is send-only.

A closed channel doesn’t block and returns true. Notice that this consumes any message available in the channel.

Examples
	ch := make(chan struct{}, 1)
	ch <- struct{}{}
	assertions.NotBlocked(t, ch)
	success:  sendChanMessage()
	failure:  make(chan struct{})
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotBlocked(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotBlocked(t *testing.T)
    	success := assert.NotBlocked(t, sendChanMessage())
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func sendChanMessage() chan struct{} {
    	ch := make(chan struct{}, 1)
    	ch <- struct{}{}
    
    	return ch
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotBlocked(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotBlocked(t *testing.T)
    	require.NotBlocked(t, sendChanMessage())
    	fmt.Println("passed")
    
    }
    
    func sendChanMessage() chan struct{} {
    	ch := make(chan struct{}, 1)
    	ch <- struct{}{}
    
    	return ch
    }

NotBlockedT[E any, CHAN ~chan E]

NotBlockedT asserts that a channel is not blocked on receive.

A closed channel doesn’t block and returns true. Notice that this consumes any message available in the channel.

Examples
	ch := make(chan struct{}, 1)
	ch <- struct{}{}
	assertions.NotBlockedT(t, ch)
	success:  sendChanMessage()
	failure:  make(chan struct{})
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotBlockedT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotBlockedT(t *testing.T)
    	success := assert.NotBlockedT(t, sendChanMessage())
    	fmt.Printf("success: %t\n", success)
    
    }
    
    func sendChanMessage() chan struct{} {
    	ch := make(chan struct{}, 1)
    	ch <- struct{}{}
    
    	return ch
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNotBlockedT(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNotBlockedT(t *testing.T)
    	require.NotBlockedT(t, sendChanMessage())
    	fmt.Println("passed")
    
    }
    
    func sendChanMessage() chan struct{} {
    	ch := make(chan struct{}, 1)
    	ch <- struct{}{}
    
    	return ch
    }


Generated with github.com/go-openapi/testify/codegen/v2