From gomega
Provides Gomega's Eventually and Consistently for polling asynchronous assertions in Go tests. Use when assertions depend on goroutines, channels, network calls, or eventual consistency.
How this skill is triggered — by the user, by Claude, or both
Slash command
/gomega:asyncThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
`Expect` asserts *now*. When the thing under test settles over time, poll it. See `gomega:overview` for the mental model and `gomega:assertions` for the synchronous side. Dot-import assumed; with plain `testing` use `g := NewWithT(t)` and call `g.Eventually(...)`. Docs: <https://onsi.github.io/gomega/#making-asynchronous-assertions>.
Expect asserts now. When the thing under test settles over time, poll it. See gomega:overview for the mental model and gomega:assertions for the synchronous side. Dot-import assumed; with plain testing use g := NewWithT(t) and call g.Eventually(...). Docs: https://onsi.github.io/gomega/#making-asynchronous-assertions.
Eventually — polls until the matcher passes or the timeout elapses. Default: poll every 10ms for up to 1s.Consistently — polls for the whole duration and fails if the matcher ever fails. Default: poll every 10ms for 100ms. Use it to assert something "does not eventually" happen.Eventually(client.FetchCount).Should(BeNumerically(">=", 17))
Consistently(channel).ShouldNot(Receive()) // nothing ever arrives
1. A bare value — rarely what you want. Gomega passes the value to the matcher every poll, but nothing re-reads it.
Eventually(c, "50ms").Should(BeClosed()) // works: matcher re-inspects the channel
This is only correct for values whose matcher re-inspects live state (channels, *gexec.Session, *gbytes.Buffer). Gotcha: Eventually(s).Should(Equal("changed")) or Eventually(slice).Should(HaveLen(3)) will not see updates — the value is captured once, and racing a goroutine over it trips the race detector. Pass a function instead.
2. A function returning at least one value. Polled repeatedly; the first return value goes to the matcher.
Eventually(func() int { return client.FetchCount() }).Should(BeNumerically(">=", 17))
The multi-return error idiom applies: extra return values must all be zero, so a (value, error) function fails the poll while err != nil and matches value once it's nil.
func FetchFromDB() (string, error)
Eventually(FetchFromDB).Should(Equal("got it")) // passes when err==nil AND string matches
Pass arguments with .WithArguments(...) (supports variadic):
Eventually(FetchFullName).WithArguments(1138).Should(Equal("Wookie"))
3. A func(g Gomega) callback — the recommended powerful form. The function makes its own assertions against the passed-in g; the whole block is retried until every assertion passes. Return zero values and match with Succeed, or return values to also match the result.
Eventually(func(g Gomega) {
model, err := client.Find(1138)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(model.Reticulate()).To(Succeed())
g.Expect(model.IsReticulated()).To(BeTrue())
}).Should(Succeed())
Eventually(func(g Gomega) (Widget, error) {
ids, err := client.FetchIDs()
g.Expect(err).NotTo(HaveOccurred())
g.Expect(ids).To(ContainElement(1138))
return client.FetchWidget(1138)
}).Should(Equal(expectedWidget))
Gotcha: inside the callback you must use the passed-in g, not the global Expect. Global assertions aren't intercepted by Eventually and will fail the test on the first miss instead of being retried.
Optional positional args (after ACTUAL): timeout, then polling interval, then context.Context. Each duration may be a time.Duration, a parseable string ("100ms"), or a float64 (interpreted as seconds).
Eventually(ACTUAL, "2s", "50ms").Should(MATCHER)
Consistently(ACTUAL, 500*time.Millisecond).Should(MATCHER)
Equivalently — and more readable — chain. Within/ProbeEvery are aliases for WithTimeout/WithPolling:
Eventually(ACTUAL).WithTimeout(2 * time.Second).WithPolling(50 * time.Millisecond).Should(MATCHER)
Eventually(ACTUAL).Within(2 * time.Second).ProbeEvery(50 * time.Millisecond).Should(MATCHER)
For Consistently the first duration is the window it must hold for, not a timeout.
.WithContext(ctx) (or passing ctx positionally, even first: Eventually(ctx, ACTUAL)) lets a cancelled context stop the poll.
Eventually(ACTUAL).WithTimeout(t).WithPolling(p).WithContext(ctx).Should(MATCHER)
Timeout × context interaction (Eventually):
ctx can govern a batch of Eventuallys. Opt out with EnforceDefaultTimeoutsWhenUsingContexts().Consistently always uses its DURATION; a cancelled context makes it bail out early as a failure, never as the duration controller.Ginkgo SpecContext plugs straight in, and Gomega auto-injects the context as the polled function's first arg (after g Gomega, if present):
It("fetches the count", func(ctx SpecContext) {
Eventually(client.FetchCount).WithContext(ctx).WithArguments("/users").Should(BeNumerically(">=", 17))
}, SpecTimeout(time.Second))
The polled function runs synchronously — Eventually cannot kill a slow function, so thread the context into it (e.g. client.FetchCount(ctx, ...)) to make it interruptible. With SpecContext, Ginkgo's Progress Reports also surface which Eventually was running and its latest failure on timeout.
Both signals work whether returned as an error or thrown via .Now() (a panic the poller catches). They also work from inside func(g Gomega) callbacks and from matchers.
StopTrying(msg) — stop polling immediately. Always a failure for Eventually.
Eventually(func() (string, error) {
if playerIndex == numPlayers {
return "", StopTrying("no more players left") // as error
}
name := client.FetchPlayer(playerIndex); playerIndex++
return name, nil
}).Should(Equal("Patrick Mahomes"))
Eventually(func() []string {
names, err := client.FetchAllPlayers()
if err == client.IRRECOVERABLE_ERROR {
StopTrying("irrecoverable error").Now() // panic form
}
return names
}).Should(ContainElement("Patrick Mahomes"))
For Consistently only, StopTrying(msg).Successfully() ends the window early without failing (e.g. the monitored goroutine has finished). Not valid with Eventually, which always treats StopTrying as failure.
Enrich the message with .Wrap(err) (renders <message>: <err>) and .Attach(description, obj) (formats obj through Gomega's formatter; repeatable).
TryAgainAfter(duration) — same return/.Now() mechanics, but instead of stopping it waits duration before the next poll. Use it to back off when a service reports "unavailable, retry later." If the overall timeout elapses during that wait, both Eventually and Consistently fail and print the message (also supports .Wrap/.Attach).
Require N consecutive passes before Eventually succeeds — useful when a single transient pass isn't enough:
Eventually(ACTUAL).MustPassRepeatedly(3).Should(MATCHER)
A matcher's Match returning StopTrying (or calling .Now()) halts polling and fails — same always-a-failure rule. Separately, a matcher implementing MatchMayChangeInTheFuture(actual) bool returning false signals out-of-band that no further change is possible, so Eventually/Consistently can stop without that being inherently a failure (e.g. Receive on a closed channel). See gomega:custom-matchers for both mechanisms.
Suite-wide, set in a BeforeSuite/TestMain:
SetDefaultEventuallyTimeout(t time.Duration)
SetDefaultEventuallyPollingInterval(t time.Duration)
SetDefaultConsistentlyDuration(t time.Duration)
SetDefaultConsistentlyPollingInterval(t time.Duration)
Or via env vars (lower precedence than the SetDefault… calls), all parseable duration strings: GOMEGA_DEFAULT_EVENTUALLY_TIMEOUT, GOMEGA_DEFAULT_EVENTUALLY_POLLING_INTERVAL, GOMEGA_DEFAULT_CONSISTENTLY_DURATION, GOMEGA_DEFAULT_CONSISTENTLY_POLLING_INTERVAL, and GOMEGA_ENFORCE_DEFAULT_TIMEOUTS_WHEN_USING_CONTEXTS.
npx claudepluginhub onsi/gomega --plugin gomegaMakes Ginkgo specs interruptible with SpecContext cancellation, timeouts, and graceful cleanup. For hanging specs, polling, streams, goroutines, or async testing with Gomega.
Writes correct synchronous Gomega assertions in Go tests: dual notation, multi-return error idiom, Succeed vs. HaveOccurred, .Error() chaining, annotation helpers, and failure-output tuning.
Fixes flaky tests by replacing arbitrary timeouts with condition polling for async operations, intermittent failures, and setTimeout/sleep delays.