Gomega Matcher Library

repository·master·Indexed 25 days ago

https://github.com/onsi/gomega

A matcher library for the Ginkgo BDD testing framework in Golang. Gomega provides a rich set of assertions and asynchronous testing tools such as Eventually and Consistently. It supports both Ω and Expect notation, handles multiple return values, and includes specialized sub-libraries like gstruct, ghttp, gexec, gbytes, gleak, and gmeasure for complex testing scenarios.

Tokens
43.2K
Snippets
109
Records
196
Agent score
77%

What's inside Gomega

  1. Gomega Matcher Catalog Overview

    master

    Gomega provides a wide variety of built-in matchers to perform assertions in Go tests. When choosing a matcher, follow these three rules:

    1. Prefer the most specific matcher: This produces significantly better failure messages.
    2. Every matcher is negatable: You can use NotTo() or ShouldNot() with any matcher.
    3. Compose freely: Many matchers accept other matchers as arguments (e.g., ContainElement(ContainSubstring("x"))).

    Note: Any matcher that accepts format string, args ...any will run fmt.Sprintf on those arguments.

  2. The Gomega mental model

    master

    Gomega is a matcher/assertion library for Go that pairs best with the Ginkgo testing framework but is compatible with any Go test. It uses a domain-specific language for assertions where matchers are treated as values that can be stored, passed, and composed.

    To use Gomega's DSL (e.g., Expect, Equal, Eventually) without qualifiers, it is conventional to dot-import the package:

    import (
    	. "github.com/onsi/gomega"
    	. "github.com/onsi/ginkgo/v2" // if using Ginkgo
    )
    import (
    	. "github.com/onsi/gomega"
    	. "github.com/onsi/ginkgo/v2"
    )
  3. Testing HTTP clients with ghttp

    master

    The ghttp package provides a wrapper around httptest.Server called ghttp.Server. It allows you to build a stack of test handlers that perform assertions against incoming requests and return pre-fabricated responses. This is ideal for testing Go HTTP clients by injecting the auto-generated server.URL() into your client.

    To use ghttp, follow this lifecycle:

    1. Initialize with ghttp.NewServer().
    2. Inject server.URL() into your client.
    3. Use server.AppendHandlers(...) to define expected requests and responses.
    4. Use server.Close() in an AfterEach block to shut down the server.
    Describe("The sprockets client", func() {
        var server *ghttp.Server
        var client *sprockets.Client
    
        BeforeEach(func() {
            server = ghttp.NewServer()
            client = sprockets.NewClient(server.URL())
        })
    
        AfterEach(func() {
            server.Close()
        })
    })
  4. Compose complex nested matchers with gstruct

    master

    The gstruct matchers are designed to be composable. You can nest MatchAllFields, MatchElements, and PointTo to perform fuzzy-matching on deeply nested structures.

    Additionally, you can use:

    • Ignore(): A matcher that always succeeds, used to skip specific fields or elements.
    • Reject(): A matcher that always fails, used to explicitly reject specific fields or elements.
    // Example of a deeply nested structure match
    Expect(actual).To(MatchAllFields(Fields{
      "Name":      Ignore(),
      "CPU": PointTo(MatchAllFields(Fields{
            "UsageNanoCores": BeNumerically("<", 1E9),
            "Cores": MatchElements(coreID, IgnoreExtras, Elements{
                "0": MatchAllFields(Fields{
                    Index: Ignore(),
                  "UsageNanoCores": BeNumerically("<", 1E9),
                }),
            }),
        })),
        "Rootfs": m.Ignore(),
    }))
  5. The shape of every assertion

    master

    Gomega assertions follow a standard pattern: Expect(ACTUAL).To(MATCHER) or Expect(ACTUAL).NotTo(MATCHER).

    • ACTUAL: The value under test.
    • MATCHER: A value satisfying the GomegaMatcher interface. Matchers are typically created by calling functions (e.g., Equal(3), HaveLen(2)).

    Alternatively, you can use the Ω notation, which is functionally identical:

    • Ω(ACTUAL).Should(MATCHER)
    • Ω(ACTUAL).ShouldNot(MATCHER)

    To/Should and NotTo/ToNot/ShouldNot are interchangeable syntactic sugar; choose one style and remain consistent.

  6. Asynchronous assertions with Eventually and Consistently

    master

    When testing code that settles over time (goroutines, channels, network calls, or eventual consistency), use polling assertions instead of standard Expect calls.

    • Eventually: Polls the subject until the matcher passes or the timeout is reached. Default: poll every 10ms for up to 1s.
    • Consistently: Polls for a specific duration and fails if the matcher ever fails during that window. Use this to assert that something does not happen. Default: poll every 10ms for 100ms.

    If you are not using Ginkgo's dot-imports, use g := NewWithT(t) and call g.Eventually(...).

    Eventually(client.FetchCount).Should(BeNumerically(">=", 17))
    Consistently(channel).ShouldNot(Receive()) // nothing ever arrives
  7. Abort Eventually/Consistently using MatchMayChangeInTheFuture

    master

    If you are using Eventually or Consistently to poll a value, you can prevent unnecessary polling if the result of a match is guaranteed not to change (e.g., a channel has been closed).

    To do this, implement the MatchMayChangeInTheFuture(actual any) bool method on your matcher.

    • If MatchMayChangeInTheFuture returns true, Eventually/Consistently will continue polling.
    • If it returns false, polling stops immediately, and the assertion either passes or fails based on the last Match result.

    Important: This only works when Eventually or Consistently are passed a bare value. If you pass them a function to be polled, Gomega cannot guarantee the return value won't change between intervals, so MatchMayChangeInTheFuture will not be called.

  8. How gstruct matchers compose

    master

    The power of gstruct lies in its ability to nest arbitrarily. You can mix gstruct matchers (for structs, slices, maps, and pointers) with core Gomega matchers and combinators (And, Or, Not, WithTransform).

    This allows you to build highly specific assertions for deeply nested, complex data structures where every level of the hierarchy requires different validation logic.

  9. Use the multi-return error idiom for concise assertions

    master

    Gomega's Expect and Ω functions accept multiple arguments. The matcher is run against the first argument, but the assertion will only pass if every subsequent argument is nil or zero-valued. This allows you to collapse Go's (value, error) pattern into a single line.

    Warning: Do not use Succeed() with a multi-return function, as Succeed() only checks the first return value. Use the .Error() chaining form instead.

  10. Use logical combinators to combine matchers

    master

    Gomega provides logical combinators to express multiple requirements in a single assertion. You can use these to create complex assertions or to build lightweight, reusable named matchers.

    • And(ms ...GomegaMatcher) or SatisfyAll(...): Succeeds only if every matcher passes. It short-circuits on the first failure.
    • Or(ms ...GomegaMatcher) or SatisfyAny(...): Succeeds if any matcher passes. It short-circuits on the first success.
    • Not(matcher GomegaMatcher): Negates the provided matcher. This is often clearer than using .NotTo() when nesting within other combinators.
    Expect(n).To(And(BeNumerically(">", 0), BeNumerically("<", 10)))   // all must pass
    Expect(msg).To(Or(Equal("Success"), MatchRegexp(`^Error .+$`)))    // any may pass
    Expect(s).To(Not(BeEmpty()))                                        // negate
  11. How the ghttp handler model works

    master

    The ghttp server uses an ordered list of handlers registered via server.AppendHandlers(handlers...).

    Core Mental Model:

    • Strict Ordering: Handlers are matched to requests in the exact order they are registered. The first request matches the first handler, the second matches the second, etc.
    • One Handler per Request: Each handler is responsible for exactly one request. If you need to perform multiple assertions (e.g., verify the path AND the body) on a single request, you must use ghttp.CombineHandlers to wrap them into a single handler.
    • Request Count Assertion: By default, the test fails if the client sends more requests than there are registered handlers, or if it hits an unregistered route. To prevent false positives when a client might not make a request, assert the count explicitly: Expect(server.ReceivedRequests()).To(HaveLen(1)).
    server.AppendHandlers(
    	ghttp.VerifyRequest("GET", "/sprockets"),
    )
  12. Synchronous vs Asynchronous assertions

    master

    Gomega distinguishes between immediate assertions and polling assertions:

    • Synchronous (Expect): Asserts the state of a value immediately.
    • Asynchronous (Eventually / Consistently): These poll a value, a function, or a g Gomega callback.
      • Eventually passes when a matcher eventually succeeds (within a timeout).
      • Consistently passes when a matcher continues to pass for a specified duration.

    Asynchronous assertions involve managing timeouts, contexts, and bail-out signals.