|
| 1 | +--- |
| 2 | +name: assertions |
| 3 | +description: Write correct synchronous Gomega assertions — Expect/Ω notation, the To/NotTo/ToNot/Should/ShouldNot equivalences, the multi-return error idiom, Succeed vs HaveOccurred, the .Error() chaining form, annotating assertions (format-string and func()string), tuning failure output via the format subpackage (MaxLength/MaxDepth/UseStringerRepresentation/GomegaStringer/TruncatedDiff/RegisterCustomFormatter/format.Object), and asserting inside helper functions with GinkgoHelper/WithOffset/ExpectWithOffset, NewWithT(t) for plain testing, and the g Gomega callback. Use when writing or reviewing synchronous (non-polling) Gomega assertions. |
| 4 | +--- |
| 5 | + |
| 6 | +# Synchronous assertions |
| 7 | + |
| 8 | +The core mechanics of asserting *now* (vs. polling — see `gomega:async`). Assumes a dot-import (`. "github.com/onsi/gomega"`). Narrative docs: <https://onsi.github.io/gomega/#making-assertions>. For the big picture and skill map see `gomega:overview`. |
| 9 | + |
| 10 | +## The two notations are identical |
| 11 | + |
| 12 | +```go |
| 13 | +Expect(ACTUAL).To(Equal(EXPECTED)) // Expect notation |
| 14 | +Expect(ACTUAL).NotTo(Equal(EXPECTED)) |
| 15 | +Expect(ACTUAL).ToNot(Equal(EXPECTED)) // ToNot == NotTo |
| 16 | + |
| 17 | +Ω(ACTUAL).Should(Equal(EXPECTED)) // Ω notation (⌥z on macOS) |
| 18 | +Ω(ACTUAL).ShouldNot(Equal(EXPECTED)) |
| 19 | +``` |
| 20 | + |
| 21 | +`To` == `Should`, and `NotTo` == `ToNot` == `ShouldNot` — pure syntactic sugar. `ACTUAL` (left) is anything; the matcher (right) must satisfy `GomegaMatcher`. Pick one notation per codebase and stay consistent. Matchers are values — see `gomega:matchers`. |
| 22 | + |
| 23 | +## The multi-return error idiom |
| 24 | + |
| 25 | +`Expect`/`Ω` accept **multiple arguments**. The matcher runs against the **first**; the assertion **fails unless every trailing argument is nil or zero-valued**. This collapses Go's `(value, error)` pattern: |
| 26 | + |
| 27 | +```go |
| 28 | +// instead of: |
| 29 | +result, err := DoSomethingHard() |
| 30 | +Expect(err).NotTo(HaveOccurred()) |
| 31 | +Expect(result).To(Equal("foo")) |
| 32 | + |
| 33 | +// write: |
| 34 | +Expect(DoSomethingHard()).To(Equal("foo")) // passes only if return is ("foo", nil) |
| 35 | +``` |
| 36 | + |
| 37 | +## Succeed and HaveOccurred |
| 38 | + |
| 39 | +For a function returning **only** `error`, use `Succeed`: |
| 40 | + |
| 41 | +```go |
| 42 | +Expect(DoSomethingSimple()).To(Succeed()) // func() error |
| 43 | +Expect(DoSomethingSimple()).NotTo(Succeed()) |
| 44 | +``` |
| 45 | + |
| 46 | +For an `error` value you already hold, use `HaveOccurred`: |
| 47 | + |
| 48 | +```go |
| 49 | +err := DoSomethingSimple() |
| 50 | +Expect(err).NotTo(HaveOccurred()) |
| 51 | +Expect(err).To(HaveOccurred()) |
| 52 | +``` |
| 53 | + |
| 54 | +Use `Succeed` when calling the function inline; use `HaveOccurred` when you have an `err` variable. To assert on the error's *content* (not just its presence), reach for `MatchError` and friends in `gomega:matchers`. |
| 55 | + |
| 56 | +**Don't** use `Succeed` with a multi-return function. The matcher only sees the *first* return value; the rest are consumed by the multi-return idiom above. `Expect(DoSomethingHard()).To(Succeed())` matches against the `string`, not the `error`, and `Expect(DoSomethingHard()).NotTo(Succeed())` can **never pass**. |
| 57 | + |
| 58 | +## The .Error() chaining form |
| 59 | + |
| 60 | +To assert on the trailing error of a **multi-return** function while ignoring the other returns, chain `.Error()`: |
| 61 | + |
| 62 | +```go |
| 63 | +Expect(MultipleReturnValuesFunc()).Error().To(HaveOccurred()) |
| 64 | +Expect(MultipleReturnValuesFunc()).Error().NotTo(HaveOccurred()) |
| 65 | +``` |
| 66 | + |
| 67 | +`.Error()` retargets the assertion at the **last** return value (the error). The `To(HaveOccurred())` form *additionally* asserts that all the *other* returns are zero-valued; the `NotTo(HaveOccurred())` form lets the other returns be anything. The plain alternative is to capture and assert manually: |
| 68 | + |
| 69 | +```go |
| 70 | +_, _, _, err := MultipleReturnValuesFunc() |
| 71 | +Expect(err).To(HaveOccurred()) |
| 72 | +``` |
| 73 | + |
| 74 | +## Annotating assertions |
| 75 | + |
| 76 | +Pass a format string (with `fmt.Sprintf` args) or a `func() string` **after** the matcher. It's printed alongside the failure message to add context: |
| 77 | + |
| 78 | +```go |
| 79 | +Expect(ACTUAL).To(Equal(EXPECTED), "my annotation %d", foo) |
| 80 | +Expect(ACTUAL).To(Equal(EXPECTED), func() string { return expensive() }) |
| 81 | +``` |
| 82 | + |
| 83 | +The `func() string` form is **lazily evaluated** — only called if the assertion fails — so use it for any annotation that's expensive to build. |
| 84 | + |
| 85 | +## Adjusting failure output |
| 86 | + |
| 87 | +On failure Gomega prints a recursive rendering of the objects involved, produced by the `format` subpackage. Tune it via package-level globals (set once, e.g. in a test helper or `TestMain`): |
| 88 | + |
| 89 | +```go |
| 90 | +import "github.com/onsi/gomega/format" |
| 91 | + |
| 92 | +format.MaxLength = 4000 // truncate rendered output to N chars; 0 disables truncation |
| 93 | +format.MaxDepth = 10 // max recursion depth into nested structures |
| 94 | +format.TruncatedDiff = true // for long strings, show only where they differ (false = full strings) |
| 95 | +format.UseStringerRepresentation = false // true => call String()/GoString() on Stringer/GoStringer types |
| 96 | +format.PrintContextObjects = false // true => print contents of context.Context objects |
| 97 | +``` |
| 98 | + |
| 99 | +`UseStringerRepresentation` defaults to `false` on purpose: a `String()` method often hides fields you need to diagnose a failure. Leave it off unless you know the stringer is complete. |
| 100 | + |
| 101 | +**`GomegaStringer` interface.** Implement `GomegaString() string` on a type and Gomega always uses it for that type's rendering, regardless of `UseStringerRepresentation`. Best practice: define it in a `_test.go` helper file so you don't leak it into your package's exported API. |
| 102 | + |
| 103 | +```go |
| 104 | +func (w Widget) GomegaString() string { return fmt.Sprintf("Widget<%s>", w.id) } |
| 105 | +``` |
| 106 | + |
| 107 | +**Custom formatters.** Register a `format.CustomFormatter` (`func(value any) (string, bool)`) — return `(rendered, true)` to handle a value, or `("", false)` to pass. Custom formatters take precedence over `GomegaStringer` and `UseStringerRepresentation`, and their output is not truncated: |
| 108 | + |
| 109 | +```go |
| 110 | +key := format.RegisterCustomFormatter(myFormatter) |
| 111 | +defer format.UnregisterCustomFormatter(key) |
| 112 | +``` |
| 113 | + |
| 114 | +**`format.Object`.** Reuse Gomega's renderer directly (the int is indentation depth): |
| 115 | + |
| 116 | +```go |
| 117 | +fmt.Println(format.Object(theThing, 1)) |
| 118 | +``` |
| 119 | + |
| 120 | +## Making assertions inside helper functions |
| 121 | + |
| 122 | +Factoring assertions into a helper reduces boilerplate, but a naive helper reports failures at the line *inside the helper*, not the caller. Fix the reported line number with one of these. |
| 123 | + |
| 124 | +**With Ginkgo — `GinkgoHelper()` (recommended).** Call it first thing in the helper; Ginkgo then attributes failures to the call site. It nests cleanly across multiple helper layers: |
| 125 | + |
| 126 | +```go |
| 127 | +func expectComponents(te TurboEncabulator, components ...string) { |
| 128 | + GinkgoHelper() |
| 129 | + got, err := te.GetComponents() |
| 130 | + Expect(err).NotTo(HaveOccurred()) |
| 131 | + Expect(got).To(ConsistOf(components)) |
| 132 | +} |
| 133 | +``` |
| 134 | + |
| 135 | +**Without Ginkgo — `WithOffset` / `ExpectWithOffset`.** The offset is how many stack frames to skip; `1` points at the helper's caller. These are equivalent: |
| 136 | + |
| 137 | +```go |
| 138 | +Expect(err).WithOffset(1).NotTo(HaveOccurred()) |
| 139 | +ExpectWithOffset(1, err).NotTo(HaveOccurred()) |
| 140 | +``` |
| 141 | + |
| 142 | +(`Eventually`/`Consistently` have the same `WithOffset` / `…WithOffset` forms — see `gomega:async`.) |
| 143 | + |
| 144 | +**Plain `testing` — `NewWithT(t)`.** Wrap a `*testing.T` to get a `*WithT` carrying `Expect`, `Eventually`, and `Consistently`. Create a fresh one per test (no global fail handler needed): |
| 145 | + |
| 146 | +```go |
| 147 | +func TestFarmHasCow(t *testing.T) { |
| 148 | + g := NewWithT(t) |
| 149 | + f := farm.New([]string{"Cow", "Horse"}) |
| 150 | + g.Expect(f.HasCow()).To(BeTrue(), "farm should have a cow") |
| 151 | +} |
| 152 | +``` |
| 153 | + |
| 154 | +**The `g Gomega` callback pattern.** A helper can accept a `Gomega` and assert through it, decoupling the helper from how failures are reported: |
| 155 | + |
| 156 | +```go |
| 157 | +func expectValidWidget(g Gomega, w Widget) { |
| 158 | + g.Expect(w.ID).NotTo(BeEmpty()) |
| 159 | + g.Expect(w.Size).To(BeNumerically(">", 0)) |
| 160 | +} |
| 161 | + |
| 162 | +g := NewWithT(t) // or the g passed into an Eventually callback |
| 163 | +expectValidWidget(g, w) |
| 164 | +``` |
| 165 | + |
| 166 | +This is the **same `g Gomega`** that `Eventually(func(g Gomega) {...})` passes into polled callbacks (`gomega:async`) — so a `g Gomega` helper works in both synchronous and polled contexts. To build a `Gomega` wired to your own fail handler, use `NewGomega(failHandler)`. |
| 167 | + |
| 168 | +**Don't** call the global `Expect`/`Ω` inside an `Eventually`/`Consistently` callback — those failures won't be intercepted by the poller. Always assert through the passed-in `g Gomega` there. See `gomega:async`. |
| 169 | + |
| 170 | +For richer, reusable assertions consider a real matcher instead of a helper — see `gomega:custom-matchers`. |
0 commit comments