Skip to content

Commit 35ca084

Browse files
committed
v1.42.0
2 parents af2bccb + d72697b commit 35ca084

19 files changed

Lines changed: 1862 additions & 3 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "gomega",
3+
"owner": {
4+
"name": "Onsi Fakhouri",
5+
"url": "https://github.com/onsi"
6+
},
7+
"metadata": {
8+
"description": "Skills for writing expressive, correct assertions with Gomega — the matcher/assertion library for Go, best paired with Ginkgo.",
9+
"homepage": "https://onsi.github.io/gomega/"
10+
},
11+
"plugins": [
12+
{
13+
"name": "gomega",
14+
"source": "./plugins/gomega",
15+
"description": "Write better Gomega assertions: the Expect/Eventually model, the full matcher catalog, composing and writing custom matchers, and the gstruct/ghttp/gexec/gbytes/gleak/gmeasure sub-libraries."
16+
}
17+
]
18+
}

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.42.0
2+
3+
Add a set of Claude skill as a marketplace plugin
4+
15
## 1.41.0
26

37
### Features

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ Jump straight to the [docs](http://onsi.github.io/gomega/) to learn about Gomega
66

77
If you have a question, comment, bug report, feature request, etc. please open a GitHub issue.
88

9+
## Using Gomega with Claude Code
10+
11+
Gomega ships a set of [Claude Code](https://claude.com/claude-code) skills as a **plugin**, so an agent writing assertions in *your* suite has Gomega's idioms — the full matcher catalog, `Eventually`/`Consistently`, and the `gstruct`/`ghttp`/`gexec`/`gbytes`/`gleak`/`gmeasure` sub-libraries — on hand. The Gomega repo doubles as the plugin marketplace, so installation is two commands. From inside Claude Code:
12+
13+
```
14+
/plugin marketplace add onsi/gomega
15+
/plugin install gomega@gomega
16+
```
17+
18+
(or non-interactively: `claude plugin marketplace add onsi/gomega` then `claude plugin install gomega@gomega`)
19+
20+
This installs a family of `gomega:*` skills that activate automatically while you write tests. See the [docs](http://onsi.github.io/gomega/#using-gomega-with-claude-code) for the full list.
21+
922
## [Ginkgo](http://github.com/onsi/ginkgo): a BDD Testing Framework for Golang
1023

1124
Learn more about Ginkgo [here](http://onsi.github.io/ginkgo/)

docs/index.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,34 @@ func TestFarmHasCow(t *testing.T) {
7171

7272
`NewWithT(t)` wraps a `*testing.T` and returns a struct that supports `Expect`, `Eventually`, and `Consistently`.
7373

74+
## Using Gomega with Claude Code
75+
76+
Gomega ships a set of [Claude Code](https://claude.com/claude-code) skills as a **plugin**, so an agent writing assertions against *your* code has Gomega's idioms on hand. The Gomega repo doubles as the plugin marketplace, so installation is two commands. From inside Claude Code:
77+
78+
```
79+
/plugin marketplace add onsi/gomega
80+
/plugin install gomega@gomega
81+
```
82+
83+
(The same can be done non-interactively with `claude plugin marketplace add onsi/gomega` and `claude plugin install gomega@gomega`.)
84+
85+
This installs a family of `gomega:*` skills that activate automatically while you write tests:
86+
87+
| Skill | What it's for |
88+
|---|---|
89+
| `gomega:overview` | The mental model — `Expect`/`Ω`, matchers-as-values, sync vs. async, and a map of the whole library. |
90+
| `gomega:assertions` | Synchronous assertions: the multi-return error idiom, `Succeed`/`HaveOccurred`, annotations, output tuning, and asserting inside helpers. |
91+
| `gomega:async` | `Eventually`/`Consistently`: polling functions, the `g Gomega` callback, contexts, timeouts, and `StopTrying`/`TryAgainAfter`. |
92+
| `gomega:matchers` | The full matcher catalog, grouped, with the gotchas flagged. |
93+
| `gomega:composing-matchers` | Combining matchers: `And`/`Or`/`Not`, `WithTransform`, `HaveField`, `HaveValue`, and nesting matchers as arguments. |
94+
| `gomega:custom-matchers` | Writing your own matcher with the `GomegaMatcher` interface and `gcustom`. |
95+
| `gomega:gstruct` | Deep, partial matching of nested structs, slices, and maps. |
96+
| `gomega:ghttp` | Testing code that makes outbound HTTP requests. |
97+
| `gomega:gexec` | Building, running, signaling, and asserting on external processes. |
98+
| `gomega:gbytes` | Asserting on streaming or incremental output. |
99+
| `gomega:gleak` | Verifying a test left no goroutines leaked. |
100+
| `gomega:gmeasure` | Human-readable benchmarks, performance reports, and regression baselines. |
101+
74102
## Making Assertions
75103

76104
Gomega provides two notations for making assertions. These notations are functionally equivalent and their differences are purely aesthetic.
@@ -2758,7 +2786,7 @@ wait for the process to exit and then make assertions against the entire content
27582786
```go
27592787
gexec.Kill() //sends SIGKILL to all processes
27602788
gexec.Terminate() //sends SIGTERM to all processes
2761-
gexec.Signal(int) //sends the passed in os.Signal signal to all the processes
2789+
gexec.Signal(os.Signal) //sends the passed in os.Signal signal to all the processes
27622790
gexec.Interrupt() //sends SIGINT to all processes
27632791
```
27642792

@@ -3451,7 +3479,7 @@ Describe("server performance", func() {
34513479
currentStats := experiment.GetStats(m)
34523480

34533481
//make sure the mean of the current performance measure is within one standard deviation of the baseline
3454-
Expect(currentStats.DurationFor(gmeasure.StatMean)).To(BeNumerically("~", baselineStats.DurationFor(gmeasure.StatsMean), baselineStats.DurationFor(gmeasure.StatsStdDev)), m)
3482+
Expect(currentStats.DurationFor(gmeasure.StatMean)).To(BeNumerically("~", baselineStats.DurationFor(gmeasure.StatMean), baselineStats.DurationFor(gmeasure.StatStdDev)), m)
34553483
}
34563484
}
34573485
})

gomega_dsl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"github.com/onsi/gomega/types"
2323
)
2424

25-
const GOMEGA_VERSION = "1.41.0"
25+
const GOMEGA_VERSION = "1.42.0"
2626

2727
const nilGomegaPanic = `You are trying to make an assertion, but haven't registered Gomega's fail handler.
2828
If you're using Ginkgo then you probably forgot to put your assertion in an It().
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "gomega",
3+
"description": "Write expressive, correct Gomega assertions in Go. Skills for the Expect/Ω model, synchronous and asynchronous (Eventually/Consistently) assertions, the full matcher catalog, composing and authoring custom matchers, and the gstruct, ghttp, gexec, gbytes, gleak, and gmeasure sub-libraries.",
4+
"version": "1.42.0",
5+
"author": {
6+
"name": "Onsi Fakhouri",
7+
"url": "https://github.com/onsi"
8+
},
9+
"homepage": "https://onsi.github.io/gomega/",
10+
"repository": "https://github.com/onsi/gomega",
11+
"license": "MIT",
12+
"keywords": ["gomega", "ginkgo", "assertions", "matchers", "testing", "go"]
13+
}

plugins/gomega/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Gomega plugin for Claude Code
2+
3+
Skills that help an AI agent (and you) write expressive, correct assertions with [Gomega](https://onsi.github.io/gomega/) — the matcher/assertion library for Go, best paired with [Ginkgo](https://github.com/onsi/ginkgo).
4+
5+
## Install
6+
7+
The Gomega repo doubles as the marketplace:
8+
9+
```
10+
/plugin marketplace add onsi/gomega
11+
/plugin install gomega@gomega
12+
```
13+
14+
## What you get
15+
16+
All skills are namespaced under `gomega:` and activate when you're writing or reviewing Go tests that use Gomega.
17+
18+
| Skill | Use it when |
19+
|---|---|
20+
| `gomega:overview` | You want the mental model — `Expect`/`Ω`, matchers-are-values, sync vs async, and a map of the whole library (read me first). |
21+
| `gomega:assertions` | You're writing synchronous assertions: the multi-return error idiom, `Succeed`/`HaveOccurred`, annotations, output tuning, and asserting inside helpers. |
22+
| `gomega:async` | You need `Eventually`/`Consistently` — polling functions, the `g Gomega` callback, contexts, timeouts, and `StopTrying`/`TryAgainAfter`. |
23+
| `gomega:matchers` | You want to find or choose the right matcher — the full catalog, grouped, with the gotchas flagged (start here to stop over-using `Equal`). |
24+
| `gomega:composing-matchers` | You're combining matchers: `And`/`Or`/`Not`, `WithTransform`, `HaveField`, `HaveValue`, and nesting matchers as arguments. |
25+
| `gomega:custom-matchers` | A built-in or composed matcher can't say what you mean and you need to write your own (`GomegaMatcher`, `gcustom`). |
26+
| `gomega:gstruct` | You're asserting against large or deeply nested structs, slices, and maps. |
27+
| `gomega:ghttp` | You're testing code that makes outbound HTTP requests. |
28+
| `gomega:gexec` | You're building, running, signaling, or asserting on external processes. |
29+
| `gomega:gbytes` | You're asserting on streaming or incremental output rather than a complete value. |
30+
| `gomega:gleak` | You want to verify a test left no goroutines leaked. |
31+
| `gomega:gmeasure` | You need human-readable benchmarks, performance reports, or regression baselines. |
32+
33+
## Versioning
34+
35+
These skills track the Gomega library. The narrative docs at <https://onsi.github.io/gomega/> are the source of truth; pin to the Gomega version you've `go get`'d.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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

Comments
 (0)