github.com/onsi/gomega@v1.32.0/docs/index.md (about) 1 --- 2 layout: default 3 title: Gomega 4 --- 5 {% raw %} 6  7 8 [Gomega](http://github.com/onsi/gomega) is a matcher/assertion library. It is best paired with the [Ginkgo](http://github.com/onsi/ginkgo) BDD test framework, but can be adapted for use in other contexts too. 9 10 ## Support Policy 11 12 Gomega provides support for versions of Go that are noted by the [Go release policy](https://golang.org/doc/devel/release.html#policy) i.e. N and N-1 major versions. 13 14 ## Getting Gomega 15 16 Just `go get` it: 17 18 ```bash 19 $ go get github.com/onsi/gomega/... 20 ``` 21 22 ## Getting Gomega as needed 23 24 Instead of getting all of Gomega and it's dependency tree, you can use the go command to get the dependencies as needed. 25 26 For example, import gomega in your test code: 27 28 ```go 29 import "github.com/onsi/gomega" 30 ``` 31 32 Use `go get -t` to retrieve the packages referenced in your test code: 33 34 ```bash 35 $ cd /path/to/my/app 36 $ go get -t ./... 37 ``` 38 39 ## Using Gomega with Ginkgo 40 41 When a Gomega assertion fails, Gomega calls a `GomegaFailHandler`. This is a function that you must provide using `gomega.RegisterFailHandler()`. 42 43 If you're using Ginkgo, all you need to do is: 44 45 ```go 46 gomega.RegisterFailHandler(ginkgo.Fail) 47 ``` 48 49 before you start your test suite. 50 51 If you use the `ginkgo` CLI to `ginkgo bootstrap` a test suite, this hookup will be automatically generated for you. 52 53 > `GomegaFailHandler` is defined in the `types` subpackage. 54 55 ## Using Gomega with Golang's XUnit-style Tests 56 57 Though Gomega is tailored to work best with Ginkgo it is easy to use Gomega with Golang's XUnit style tests. Here's how: 58 59 To use Gomega with Golang's XUnit style tests: 60 61 ```go 62 func TestFarmHasCow(t *testing.T) { 63 g := NewWithT(t) 64 65 f := farm.New([]string{"Cow", "Horse"}) 66 g.Expect(f.HasCow()).To(BeTrue(), "Farm should have cow") 67 } 68 ``` 69 70 `NewWithT(t)` wraps a `*testing.T` and returns a struct that supports `Expect`, `Eventually`, and `Consistently`. 71 72 ## Making Assertions 73 74 Gomega provides two notations for making assertions. These notations are functionally equivalent and their differences are purely aesthetic. 75 76 - When you use the `Ω` notation, your assertions look like this: 77 78 ```go 79 Ω(ACTUAL).Should(Equal(EXPECTED)) 80 Ω(ACTUAL).ShouldNot(Equal(EXPECTED)) 81 ``` 82 83 - When you use the `Expect` notation, your assertions look like this: 84 85 ```go 86 Expect(ACTUAL).To(Equal(EXPECTED)) 87 Expect(ACTUAL).NotTo(Equal(EXPECTED)) 88 Expect(ACTUAL).ToNot(Equal(EXPECTED)) 89 ``` 90 91 On OS X the `Ω` character should be easy to type, it is usually just option-z: `⌥z`. 92 93 On the left hand side, you can pass anything you want in to `Ω` and `Expect` for `ACTUAL`. On the right hand side you must pass an object that satisfies the `GomegaMatcher` interface. Gomega's matchers (e.g. `Equal(EXPECTED)`) are simply functions that create and initialize an appropriate `GomegaMatcher` object. 94 95 > Note that `Should` and `To` are just syntactic sugar and are functionally identical. Same is the case for `ToNot` and `NotTo`. 96 97 > The `GomegaMatcher` interface is pretty simple and is discussed in the [custom matchers](#adding-your-own-matchers) section. It is defined in the `types` subpackage. 98 99 ### Handling Errors 100 101 It is a common pattern, in Golang, for functions and methods to return two things - a value and an error. For example: 102 103 ```go 104 func DoSomethingHard() (string, error) { 105 ... 106 } 107 ``` 108 109 To assert on the return value of such a method you might write a test that looks like this: 110 111 ```go 112 result, err := DoSomethingHard() 113 Ω(err).ShouldNot(HaveOccurred()) 114 Ω(result).Should(Equal("foo")) 115 ``` 116 117 Gomega streamlines this very common use case. Both `Ω` and `Expect` accept *multiple* arguments. The first argument is passed to the matcher, and the match only succeeds if *all* subsequent arguments are `nil` or zero-valued. With this, we can rewrite the above example as: 118 119 ```go 120 Ω(DoSomethingHard()).Should(Equal("foo")) 121 ``` 122 123 This will only pass if the return value of `DoSomethingHard()` is `("foo", nil)`. 124 125 Additionally, if you call a function with a single `error` return value you can use the `Succeed` matcher to assert the function has returned without error. So for a function of the form: 126 127 ```go 128 func DoSomethingSimple() error { 129 ... 130 } 131 ``` 132 133 You can either write: 134 135 ```go 136 err := DoSomethingSimple() 137 Ω(err).ShouldNot(HaveOccurred()) 138 ``` 139 140 Or you can write: 141 142 ```go 143 Ω(DoSomethingSimple()).Should(Succeed()) 144 ``` 145 146 > You should not use a function with multiple return values (like `DoSomethingHard`) with `Succeed`. Matchers are only passed the *first* value provided to `Ω`/`Expect`, the subsequent arguments are handled by `Ω` and `Expect` as outlined above. As a result of this behavior `Ω(DoSomethingHard()).ShouldNot(Succeed())` would never pass. 147 148 Assertions about errors on functions with multiple return values can be made as follows (and in a lazy way when not asserting that all other return values are zero values): 149 150 ```go 151 _, _, _, err := MultipleReturnValuesFunc() 152 Ω(err).Should(HaveOccurred()) 153 ``` 154 155 Alternatively, such error assertions on multi return value functions can be simplified by chaining `Error` to `Ω` and `Expect`. Doing so will additionally automatically assert that all return values, except for the trailing error return value, are in fact zero values: 156 157 ```go 158 Ω(MultipleReturnValuesFunc()).Error().Should(HaveOccurred()) 159 ``` 160 161 Similar, asserting that no error occurred is supported, too (where the other return values are allowed to take on any value): 162 163 ```go 164 Ω(MultipleReturnValuesFunc()).Error().ShouldNot(HaveOccurred()) 165 ``` 166 167 ### Annotating Assertions 168 169 You can annotate any assertion by passing either a format string (and optional inputs to format) or a function of type `func() string` after the `GomegaMatcher`: 170 171 ```go 172 Ω(ACTUAL).Should(Equal(EXPECTED), "My annotation %d", foo) 173 Ω(ACTUAL).ShouldNot(Equal(EXPECTED), "My annotation %d", foo) 174 Expect(ACTUAL).To(Equal(EXPECTED), "My annotation %d", foo) 175 Expect(ACTUAL).NotTo(Equal(EXPECTED), "My annotation %d", foo) 176 Expect(ACTUAL).ToNot(Equal(EXPECTED), "My annotation %d", foo) 177 Expect(ACTUAL).To(Equal(EXPECTED), func() string { return "My annotation" }) 178 ``` 179 180 If you pass a format string, the format string and inputs will be passed to `fmt.Sprintf(...)`. 181 If you instead pass a function, the function will be lazily evaluated if the assertion fails. 182 In both cases, if the assertion fails, Gomega will print your annotation alongside its standard failure message. 183 184 This is useful in cases where the standard failure message lacks context. For example, if the following assertion fails: 185 186 ```go 187 Ω(SprocketsAreLeaky()).Should(BeFalse()) 188 ``` 189 190 Gomega will output: 191 192 ``` 193 Expected 194 <bool>: true 195 to be false 196 ``` 197 198 But this assertion: 199 200 ```go 201 Ω(SprocketsAreLeaky()).Should(BeFalse(), "Sprockets shouldn't leak") 202 ``` 203 204 Will offer the more helpful output: 205 206 ``` 207 Sprockets shouldn't leak 208 Expected 209 <bool>: true 210 to be false 211 ``` 212 213 214 ### Adjusting Output 215 216 When a failure occurs, Gomega prints out a recursive description of the objects involved in the failed assertion. This output can be very verbose, but Gomega's philosophy is to give as much output as possible to aid in identifying the root cause of a test failure. 217 218 These recursive object renditions are performed by the `format` subpackage. Import the format subpackage in your test code: 219 220 ```go 221 import "github.com/onsi/gomega/format" 222 ``` 223 224 `format` provides some globally adjustable settings to tune Gomega's output: 225 226 - `format.MaxLength = 4000`: Gomega will recursively traverse nested data structures as it produces output. If the length of this string representation is more than MaxLength, it will be truncated to MaxLength. To disable this behavior, set the MaxLength to `0`. 227 - `format.MaxDepth = 10`: Gomega will recursively traverse nested data structures as it produces output. By default the maximum depth of this recursion is set to `10` you can adjust this to see deeper or shallower representations of objects. 228 - Implementing `format.GomegaStringer`: If `GomegaStringer` interface is implemented on an object, Gomega will call `GomegaString` for an object's string representation. This is regardless of the `format.UseStringerRepresentation` value. Best practice to implement this interface is to implement it in a helper test file (e.g. `helper_test.go`) to avoid leaking it to your package's exported API. 229 - `format.UseStringerRepresentation = false`: Gomega does *not* call `String` or `GoString` on objects that satisfy the `Stringer` and `GoStringer` interfaces. Oftentimes such representations, while more human readable, do not contain all the relevant information associated with an object thereby making it harder to understand why a test might be failing. If you'd rather see the output of `String` or `GoString` set this property to `true`. 230 231 > For a tricky example of why `format.UseStringerRepresentation = false` is your friend, check out issue [#37](https://github.com/onsi/gomega/issues/37). 232 233 - `format.PrintContextObjects = false`: Gomega by default will not print the content of objects satisfying the context.Context interface, due to too much output. If you want to enable displaying that content, set this property to `true`. 234 235 If you want to use Gomega's recursive object description in your own code you can call into the `format` package directly: 236 237 ```go 238 fmt.Println(format.Object(theThingYouWantToPrint, 1)) 239 ``` 240 241 - `format.TruncatedDiff = true`: Gomega will truncate long strings and only show where they differ. You can set this to `false` if 242 you want to see the full strings. 243 244 You can also register your own custom formatter using `format.RegisterCustomFormatter(f)`. Custom formatters must be of type `type CustomFormatter func(value interface{}) (string, bool)`. Gomega will pass in any objects to be formatted to each registered custom formatter. A custom formatter signals that it will handle the passed-in object by returning a formatted string and `true`. If it does not handle the object it should return `"", false`. Strings returned by custom formatters will _not_ be truncated (though they may be truncated if the object being formatted is within another struct). Custom formatters take precedence of `GomegaStringer` and `format.UseStringerRepresentation`. 245 246 `format.RegisterCustomFormatter` returns a key that can be used to unregister the custom formatter: 247 248 ```go 249 key := format.RegisterCustomFormatter(myFormatter) 250 ... 251 format.UnregisterCustomFormatter(key) 252 ``` 253 254 ## Making Asynchronous Assertions 255 256 Gomega has support for making *asynchronous* assertions. There are two functions that provide this support: `Eventually` and `Consistently`. 257 258 ### Eventually 259 260 `Eventually` checks that an assertion *eventually* passes. `Eventually` blocks when called and attempts an assertion periodically until it passes or a timeout occurs. Both the timeout and polling interval are configurable as optional arguments: 261 262 ```go 263 Eventually(ACTUAL, (TIMEOUT), (POLLING_INTERVAL), (context.Context)).Should(MATCHER) 264 ``` 265 266 The first optional argument is the timeout (which defaults to 1s), the second is the polling interval (which defaults to 10ms). Both intervals can be specified as time.Duration, parsable duration strings (e.g. "100ms") or `float64` (in which case they are interpreted as seconds). You can also provide a `context.Context` which - when cancelled - will instruct `Eventually` to stop and exit with a failure message. You are also allowed to pass in the `context.Context` _first_ as `Eventually(ctx, ACTUAL)`. 267 268 > As with synchronous assertions, you can annotate asynchronous assertions by passing either a format string and optional inputs or a function of type `func() string` after the `GomegaMatcher`. 269 270 Alternatively, the timeout and polling interval can also be specified by chaining `Within` and `ProbeEvery` or `WithTimeout` and `WithPolling` to `Eventually`: 271 272 ```go 273 Eventually(ACTUAL).WithTimeout(TIMEOUT).WithPolling(POLLING_INTERVAL).Should(MATCHER) 274 Eventually(ACTUAL).Within(TIMEOUT).ProbeEvery(POLLING_INTERVAL).Should(MATCHER) 275 ``` 276 277 You can also configure the context in this way: 278 279 ```go 280 Eventually(ACTUAL).WithTimeout(TIMEOUT).WithPolling(POLLING_INTERVAL).WithContext(ctx).Should(MATCHER) 281 ``` 282 283 When no explicit timeout is provided, `Eventually` will use the default timeout. However if no explicit timeout is provided _and_ a context is provided, `Eventually` will not apply a timeout but will instead keep trying until the context is cancelled. If both a context and a timeout are provided, `Eventually` will keep trying until either the context is cancelled or time runs out, whichever comes first. 284 285 You can also ensure a number of consecutive pass before continuing with `MustPassRepeatedly`: 286 287 ```go 288 Eventually(ACTUAL).MustPassRepeatedly(NUMBER).Should(MATCHER) 289 ``` 290 291 Eventually works with any Gomega compatible matcher and supports making assertions against three categories of `ACTUAL` value: 292 293 #### Category 1: Making `Eventually` assertions on values 294 295 There are several examples of values that can change over time. These can be passed in to `Eventually` and will be passed to the matcher repeatedly until a match occurs. For example: 296 297 ```go 298 c := make(chan bool) 299 go DoStuff(c) 300 Eventually(c, "50ms").Should(BeClosed()) 301 ``` 302 303 will poll the channel repeatedly until it is closed. In this example `Eventually` will block until either the specified timeout of 50ms has elapsed or the channel is closed, whichever comes first. 304 305 Several Gomega libraries allow you to use Eventually in this way. For example, the `gomega/gexec` package allows you to block until a `*gexec.Session` exits successfully via: 306 307 ```go 308 Eventually(session).Should(gexec.Exit(0)) 309 ``` 310 311 And the `gomega/gbytes` package allows you to monitor a streaming `*gbytes.Buffer` until a given string is seen: 312 313 ```go 314 Eventually(buffer).Should(gbytes.Say("hello there")) 315 ``` 316 317 In these examples, both `session` and `buffer` are designed to be thread-safe when polled by the `Exit` and `Say` matchers. This is not true in general of most raw values, so while it is tempting to do something like: 318 319 ```go 320 /* === INVALID === */ 321 var s *string 322 go mutateStringEventually(s) 323 Eventually(s).Should(Equal("I've changed")) 324 ``` 325 326 this will trigger Go's race detector as the goroutine polling via Eventually will race over the value of `s` with the goroutine mutating the string. 327 328 Similarly, something like `Eventually(slice).Should(HaveLen(N))` probably won't do what you think it should -- `Eventually` will be passed a pointer to the slice, yes, but if the slice is being `append`ed to (as in: `slice = append(slice, ...)`) Go will generate a new pointer and the pointer passed to `Eventually` will not contain the new elements. 329 330 In both cases you should always pass `Eventually` a function that, when polled, returns the latest value of the object in question in a thread-safe way. 331 332 #### Category 2: Making `Eventually` assertions on functions 333 334 `Eventually` can be passed functions that **return at least one value**. When configured this way, `Eventually` will poll the function repeatedly and pass the first returned value to the matcher. 335 336 For example: 337 338 ```go 339 Eventually(func() int { 340 return client.FetchCount() 341 }).Should(BeNumerically(">=", 17)) 342 ``` 343 344 will repeatedly poll `client.FetchCount` until the `BeNumerically` matcher is satisfied. 345 346 > Note that this example could have been written as `Eventually(client.FetchCount).Should(BeNumerically(">=", 17))` 347 348 If multiple values are returned by the function, `Eventually` will pass the first value to the matcher and require that all others are zero-valued. This allows you to pass `Eventually` a function that returns a value and an error - a common pattern in Go. 349 350 For example, consider a method that returns a value and an error: 351 352 ```go 353 func FetchFromDB() (string, error) 354 ``` 355 356 Then 357 358 ```go 359 Eventually(FetchFromDB).Should(Equal("got it")) 360 ``` 361 362 will pass only if and when the returned error is `nil` *and* the returned string satisfies the matcher. 363 364 365 Eventually can also accept functions that take arguments, however you must provide those arguments using `Eventually().WithArguments()`. For example, consider a function that takes a user-id and makes a network request to fetch a full name: 366 367 ```go 368 func FetchFullName(userId int) (string, error) 369 ``` 370 371 You can poll this function like so: 372 373 ```go 374 Eventually(FetchFullName).WithArguments(1138).Should(Equal("Wookie")) 375 ``` 376 377 `WithArguments()` supports multiple arguments as well as variadic arguments. 378 379 It is important to note that the function passed into Eventually is invoked **synchronously** when polled. `Eventually` does not (in fact, it cannot) kill the function if it takes longer to return than `Eventually`'s configured timeout. This is where using a `context.Context` can be helpful. Here is an example that leverages Gingko's support for interruptible nodes and spec timeouts: 380 381 ```go 382 It("fetches the correct count", func(ctx SpecContext) { 383 Eventually(func() int { 384 return client.FetchCount(ctx, "/users") 385 }, ctx).Should(BeNumerically(">=", 17)) 386 }, SpecTimeout(time.Second)) 387 ``` 388 389 now when the spec times out both the `client.FetchCount` function and `Eventually` will be signaled and told to exit. you can also use `Eventually().WithContext(ctx)` to provide the context. 390 391 392 Since functions that take a context.Context as a first-argument are common in Go, `Eventually` supports automatically injecting the provided context into the function. This plays nicely with `WithArguments()` as well. You can rewrite the above example as: 393 394 ```go 395 It("fetches the correct count", func(ctx SpecContext) { 396 Eventually(client.FetchCount).WithContext(ctx).WithArguments("/users").Should(BeNumerically(">=", 17)) 397 }, SpecTimeout(time.Second)) 398 ``` 399 400 now the `ctx` `SpecContext` is used both by `Eventually` and `client.FetchCount` and the `"/users"` argument is passed in after the `ctx` argument. 401 402 The use of a context also allows you to specify a single timeout across a collection of `Eventually` assertions: 403 404 ```go 405 It("adds a few books and checks the count", func(ctx SpecContext) { 406 intialCount := client.FetchCount(ctx, "/items") 407 client.AddItem(ctx, "foo") 408 client.AddItem(ctx, "bar") 409 //note that there are several supported ways to pass in the context. All are equivalent: 410 Eventually(ctx, client.FetchCount).WithArguments("/items").Should(BeNumerically("==", initialCount + 2)) 411 Eventually(client.FetchItems).WithContext(ctx).Should(ContainElement("foo")) 412 Eventually(client.FetchItems, ctx).Should(ContainElement("foo")) 413 }, SpecTimeout(time.Second * 5)) 414 ``` 415 416 In addition, Gingko's `SpecContext` allows Gomega to tell Ginkgo about the status of a currently running `Eventually` whenever a Progress Report is generated. So, if a spec times out while running an `Eventually` Ginkgo will not only show you which `Eventually` was running when the timeout occurred, but will also include the failure the `Eventually` was hitting when the timeout occurred. 417 418 #### Category 3: Making assertions _in_ the function passed into `Eventually` 419 420 When testing complex systems it can be valuable to assert that a *set* of assertions passes `Eventually`. `Eventually` supports this by accepting functions that take **a single `Gomega` argument** and **return zero or more values**. 421 422 Here's an example that makes some assertions and returns a value and error: 423 424 ```go 425 Eventually(func(g Gomega) (Widget, error) { 426 ids, err := client.FetchIDs() 427 g.Expect(err).NotTo(HaveOccurred()) 428 g.Expect(ids).To(ContainElement(1138)) 429 return client.FetchWidget(1138) 430 }).Should(Equal(expectedWidget)) 431 ``` 432 433 will pass only if all the assertions in the polled function pass and the return value satisfied the matcher. Note that the assertions in the body of the polled function must be performed using the passed-in `g Gomega` object. If you use the global DSL expectations, `Eventually` will not intercept any failures and the test will fail. 434 435 `Eventually` also supports a special case polling function that takes a single `Gomega` argument and returns no values. `Eventually` assumes such a function is making assertions and is designed to work with the `Succeed` matcher to validate that all assertions have passed. 436 437 For example: 438 439 ```go 440 Eventually(func(g Gomega) { 441 model, err := client.Find(1138) 442 g.Expect(err).NotTo(HaveOccurred()) 443 g.Expect(model.Reticulate()).To(Succeed()) 444 g.Expect(model.IsReticulated()).To(BeTrue()) 445 g.Expect(model.Save()).To(Succeed()) 446 }).Should(Succeed()) 447 ``` 448 449 will rerun the function until all assertions pass. 450 451 You can also pass additional arguments to functions that take a Gomega. The only rule is that the Gomega argument must be first. If you also want to pass the context attached to `Eventually` you must ensure that is the second argument. For example: 452 453 ```go 454 Eventually(func(g Gomega, ctx context.Context, path string, expected ...string){ 455 tok, err := client.GetToken(ctx) 456 g.Expect(err).NotTo(HaveOccurred()) 457 458 elements, err := client.Fetch(ctx, tok, path) 459 g.Expect(err).NotTo(HaveOccurred()) 460 g.Expect(elements).To(ConsistOf(expected)) 461 }).WithContext(ctx).WithArguments("/names", "Joe", "Jane", "Sam").Should(Succeed()) 462 ``` 463 464 ### Consistently 465 466 `Consistently` checks that an assertion passes for a period of time. It does this by polling its argument repeatedly during the period. It fails if the matcher ever fails during that period. 467 468 For example: 469 470 ```go 471 Consistently(func() []int { 472 return thing.MemoryUsage() 473 }).Should(BeNumerically("<", 10)) 474 ``` 475 476 `Consistently` will poll the passed in function repeatedly and check the return value against the `GomegaMatcher`. `Consistently` blocks and only returns when the desired duration has elapsed or if the matcher fails or if an (optional) passed-in context is cancelled. The default value for the wait-duration is 100 milliseconds. The default polling interval is 10 milliseconds. Like `Eventually`, you can change these values by passing them in just after your function: 477 478 ```go 479 Consistently(ACTUAL, (DURATION), (POLLING_INTERVAL), (context.Context)).Should(MATCHER) 480 ``` 481 482 As with `Eventually`, the duration parameters can be `time.Duration`s, string representations of a `time.Duration` (e.g. `"200ms"`) or `float64`s that are interpreted as seconds. 483 484 Also as with `Eventually`, `Consistently` supports chaining `WithTimeout`, `WithPolling`, `WithContext` and `WithArguments` in the form of: 485 486 ```go 487 Consistently(ACTUAL).WithTimeout(DURATION).WithPolling(POLLING_INTERVAL).WithContext(ctx).WithArguments(...).Should(MATCHER) 488 ``` 489 490 `Consistently` tries to capture the notion that something "does not eventually" happen. A common use-case is to assert that no goroutine writes to a channel for a period of time. If you pass `Consistently` an argument that is not a function, it simply passes that argument to the matcher. So we can assert that: 491 492 ```go 493 Consistently(channel).ShouldNot(Receive()) 494 ``` 495 496 To assert that nothing gets sent to a channel. 497 498 As with `Eventually`, you can also pass `Consistently` a function. In fact, `Consistently` works with the three categories of `ACTUAL` value outlined for `Eventually` in the section above. 499 500 If `Consistently` is passed a `context.Context` it will exit if the context is cancelled - however it will always register the cancellation of the context as a failure. That is, the context is not used to control the duration of `Consistently` - that is always done by the `DURATION` parameter; instead, the context is used to allow `Consistently` to bail out early if it's time for the spec to finish up (e.g. a timeout has elapsed, or the user has sent an interrupt signal). 501 502 When no explicit duration is provided, `Consistently` will use the default duration. Unlike `Eventually`, this behavior holds whether or not a context is provided. 503 504 > Developers often try to use `runtime.Gosched()` to nudge background goroutines to run. This can lead to flaky tests as it is not deterministic that a given goroutine will run during the `Gosched`. `Consistently` is particularly handy in these cases: it polls for 100ms which is typically more than enough time for all your Goroutines to run. Yes, this is basically like putting a time.Sleep() in your tests... Sometimes, when making negative assertions in a concurrent world, that's the best you can do! 505 506 ### Bailing Out Early - Polling Functions 507 508 There are cases where you need to signal to `Eventually` and `Consistently` that they should stop trying. Gomega provides`StopTrying(message string)` to allow you to send that signal. There are two ways to use `StopTrying`. 509 510 First, you can return `StopTrying` as an error. Consider, for example, the case where `Eventually` is searching through a set of possible queries with a server: 511 512 ```go 513 playerIndex, numPlayers := 0, 11 514 Eventually(func() (string, error) { 515 if playerIndex == numPlayers { 516 return "", StopTrying("no more players left") 517 } 518 name := client.FetchPlayer(playerIndex) 519 playerIndex += 1 520 return name, nil 521 }).Should(Equal("Patrick Mahomes")) 522 ``` 523 524 Here we return a `StopTrying` error to tell `Eventually` that we've looked through all possible players and that it should stop. 525 526 You can also call `StopTrying(...).Now()` to immediately end execution of the function. Consider, for example, the case of a client communicating with a server that experiences an irrevocable error: 527 528 ```go 529 Eventually(func() []string { 530 names, err := client.FetchAllPlayers() 531 if err == client.IRRECOVERABLE_ERROR { 532 StopTrying("An irrecoverable error occurred").Now() 533 } 534 return names 535 }).Should(ContainElement("Patrick Mahomes")) 536 ``` 537 538 calling `.Now()` will trigger a panic that will signal to `Eventually` that it should stop trying. 539 540 You can also return `StopTrying()` errors and use `StopTrying().Now()` with `Consistently`. 541 542 Both `Eventually` and `Consistently` always treat the `StopTrying()` signal as a failure. The failure message will include the message passed in to `StopTrying()`. 543 544 You can add additional information to this failure message in a few ways. You can wrap an error via `StopTrying(message).Wrap(wrappedErr)` - now the output will read `<message>: <wrappedErr.Error()>`. 545 546 You can also attach arbitrary objects to `StopTrying()` via `StopTrying(message).Attach(description string, object any)`. Gomega will run the object through Gomega's standard formatting library to build a consistent representation for end users. You can attach multiple objects in this way and the output will look like: 547 548 ``` 549 Told to stop trying after <X> 550 551 <message>: <wrappedErr.Error()> 552 <description>: 553 <formatted-object> 554 <description>: 555 <formatted-object> 556 ``` 557 558 ### Bailing Out Early - Matchers 559 560 Just like functions being polled, matchers can also indicate if `Eventually`/`Consistently` should stop polling. Matchers implement a `Match` method with the following signature: 561 562 ```go 563 Match(actual interface{}) (success bool, err error) 564 ``` 565 566 If a matcher returns `StopTrying` for `error`, or calls `StopTrying(...).Now()`, `Eventually` and `Consistently` will stop polling and fail: `StopTrying` **always** signifies a failure. 567 568 > Note: An alternative mechanism for having matchers bail out early is documented in the [custom matchers section below](#aborting-eventuallyconsistently). This mechanism, which entails implementing a `MatchMayChangeIntheFuture(<actual>) bool` method, allows matchers to signify that no future change is possible out-of-band of the call to the matcher. 569 570 ### Changing the Polling Interval Dynamically 571 572 You typically configure the polling interval for `Eventually` and `Consistently` using the `.WithPolling()` or `.ProbeEvery()` chaining methods. Sometimes, however, a polled function or matcher might want to signal that a service is unavailable but should be tried again after a certain duration. 573 574 You can signal this to both `Eventually` and `Consistently` using `TryAgainAfter(<duration>)`. This error-signal operates like `StopTrying()`: you can return `TryAgainAfter(<duration>)` as an error or throw a panic via `TryAgainAfter(<duration>).Now()`. In either case, both `Eventually` and `Consistently` will wait for the specified duration before trying again. 575 576 If a timeout occurs after the `TryAgainAfter` signal is sent but _before_ the next poll occurs both `Eventually` _and_ `Consistently` will always fail and print out the content of `TryAgainAfter`. The default message is `"told to try again after <duration>"` however, as with `StopTrying` you can use `.Wrap()` and `.Attach()` to wrap an error and attach additional objects to include in the message, respectively. 577 578 ### Modifying Default Intervals 579 580 By default, `Eventually` will poll every 10 milliseconds for up to 1 second and `Consistently` will monitor every 10 milliseconds for up to 100 milliseconds. You can modify these defaults across your test suite with: 581 582 ```go 583 SetDefaultEventuallyTimeout(t time.Duration) 584 SetDefaultEventuallyPollingInterval(t time.Duration) 585 SetDefaultConsistentlyDuration(t time.Duration) 586 SetDefaultConsistentlyPollingInterval(t time.Duration) 587 ``` 588 589 You can also adjust these global timeouts by setting the `GOMEGA_DEFAULT_EVENTUALLY_TIMEOUT`, `GOMEGA_DEFAULT_EVENTUALLY_POLLING_INTERVAL`, `GOMEGA_DEFAULT_CONSISTENTLY_DURATION`, and `GOMEGA_DEFAULT_CONSISTENTLY_POLLING_INTERVAL` environment variables to a parseable duration string. The environment variables have a lower precedence than `SetDefault...()`. 590 591 ## Making Assertions in Helper Functions 592 593 While writing [custom matchers](#adding-your-own-matchers) is an expressive way to make assertions against your code, it is often more convenient to write one-off helper functions like so: 594 595 ```go 596 var _ = Describe("Turbo-encabulator", func() { 597 ... 598 func assertTurboEncabulatorContains(components ...string) { 599 teComponents, err := turboEncabulator.GetComponents() 600 Expect(err).NotTo(HaveOccurred()) 601 602 Expect(teComponents).To(HaveLen(components)) 603 for _, component := range components { 604 Expect(teComponents).To(ContainElement(component)) 605 } 606 } 607 608 It("should have components", func() { 609 assertTurboEncabulatorContains("semi-boloid slots", "grammeters") 610 }) 611 }) 612 ``` 613 614 This makes your tests more expressive and reduces boilerplate. However, when an assertion in the helper fails the line numbers provided by Gomega are unhelpful. Instead of pointing you to the line in your test that failed, they point you the line in the helper. 615 616 To fix this, Ginkgo and Gomega provide two options. If you are on a recent version of Ginkgo you can register your helper with Ginkgo via `GinkgoHelper()`: 617 618 ```go 619 func assertTurboEncabulatorContains(components ...string) { 620 GinkgoHelper() 621 teComponents, err := turboEncabulator.GetComponents() 622 Expect(err).NotTo(HaveOccurred()) 623 624 Expect(teComponents).To(HaveLen(components)) 625 for _, component := range components { 626 Expect(teComponents).To(ContainElement(component)) 627 } 628 } 629 ``` 630 631 now, line numbers generated by Ginkgo will skip `assertTurboEncabulatorContains` and point to the calling site instead. `GinkgoHelper()` is the recommended way to solve this problem as it allows for straightforward nesting and reuse of helper functions. 632 633 If, for some reason, you can't use `GinkgoHelper()` Gomega does provide an alternative: versions of `Expect`, `Eventually` and `Consistently` named `ExpectWithOffset`, `EventuallyWithOffset` and `ConsistentlyWithOffset` that allow you to specify an *offset* in the call stack. The offset is the first argument to these functions. 634 635 With this, we can rewrite our helper as: 636 637 ```go 638 func assertTurboEncabulatorContains(components ...string) { 639 teComponents, err := turboEncabulator.GetComponents() 640 ExpectWithOffset(1, err).NotTo(HaveOccurred()) 641 642 ExpectWithOffset(1, teComponents).To(HaveLen(components)) 643 for _, component := range components { 644 ExpectWithOffset(1, teComponents).To(ContainElement(component)) 645 } 646 } 647 ``` 648 649 Now, failed assertions will point to the correct call to the helper in the test. 650 651 Alternatively, you can just use the baseline versions of `Expect`, `Eventually` and `Consistently` and combine them with `WithOffset`: 652 653 ```go 654 func assertTurboEncabulatorContains(components ...string) { 655 teComponents, err := turboEncabulator.GetComponents() 656 Expect(err).WithOffset(1).NotTo(HaveOccurred()) 657 658 Expect(teComponents).WithOffset(1).To(HaveLen(components)) 659 for _, component := range components { 660 Expect(teComponents).WithOffset(1).To(ContainElement(component)) 661 } 662 } 663 ``` 664 665 Again, we recommend using `GinkgoHelper()` instead of `WithOffset(...)`. 666 667 ## Provided Matchers 668 669 Gomega comes with a bunch of `GomegaMatcher`s. They're all documented here. If there's one you'd like to see written either [send a pull request or open an issue](http://github.com/onsi/gomega). 670 671 A number of community-supported matchers have appeared as well. A list is maintained on the Gomega [wiki](https://github.com/onsi/gomega/wiki). 672 673 These docs only go over the positive assertion case (`Should`), the negative case (`ShouldNot`) is simply the negation of the positive case. They also use the `Ω` notation, but - as mentioned above - the `Expect` notation is equivalent. 674 675 ### Asserting Equivalence 676 677 #### Equal(expected interface{}) 678 679 ```go 680 Ω(ACTUAL).Should(Equal(EXPECTED)) 681 ``` 682 683 uses [`reflect.DeepEqual`](http://golang.org/pkg/reflect#deepequal) to compare `ACTUAL` with `EXPECTED`. 684 685 `reflect.DeepEqual` is awesome. It will use `==` when appropriate (e.g. when comparing primitives) but will recursively dig into maps, slices, arrays, and even your own structs to ensure deep equality. `reflect.DeepEqual`, however, is strict about comparing types. Both `ACTUAL` and `EXPECTED` *must* have the same type. If you want to compare across different types (e.g. if you've defined a type alias) you should use `BeEquivalentTo`. 686 687 It is an error for both `ACTUAL` and `EXPECTED` to be nil, you should use `BeNil()` instead. 688 689 When both `ACTUAL` and `EXPECTED` are a very long strings, it will attempt to pretty-print the diff and display exactly where they differ. 690 691 > For asserting equality between numbers of different types, you'll want to use the [`BeNumerically()`](#benumericallycomparator-string-compareto-interface) matcher. 692 693 #### BeComparableTo(expected interface{}, options ...cmp.Option) 694 695 ```go 696 Ω(ACTUAL).Should(BeComparableTo(EXPECTED, options ...cmp.Option)) 697 ``` 698 699 uses [`gocmp.Equal`](http://github.com/google/go-cmp) from `github.com/google/go-cmp` to compare `ACTUAL` with `EXPECTED`. This performs a deep object comparison like `reflect.DeepEqual` but offers a few additional configuration options. Learn more at the [go-cmp godocs](https://pkg.go.dev/github.com/google/go-cmp). 700 701 #### BeEquivalentTo(expected interface{}) 702 703 ```go 704 Ω(ACTUAL).Should(BeEquivalentTo(EXPECTED)) 705 ``` 706 707 Like `Equal`, `BeEquivalentTo` uses `reflect.DeepEqual` to compare `ACTUAL` with `EXPECTED`. Unlike `Equal`, however, `BeEquivalentTo` will first convert `ACTUAL`'s type to that of `EXPECTED` before making the comparison with `reflect.DeepEqual`. 708 709 This means that `BeEquivalentTo` will successfully match equivalent values of different types. This is particularly useful, for example, with type aliases: 710 711 ```go 712 type FoodSrce string 713 714 Ω(FoodSrce("Cheeseboard Pizza") 715 ).Should(Equal("Cheeseboard Pizza")) //will fail 716 Ω(FoodSrce("Cheeseboard Pizza") 717 ).Should(BeEquivalentTo("Cheeseboard Pizza")) //will pass 718 ``` 719 720 As with `Equal` it is an error for both `ACTUAL` and `EXPECTED` to be nil, you should use `BeNil()` instead. 721 722 As a rule, you **should not** use `BeEquivalentTo` with numbers. Both of the following assertions are true: 723 724 ```go 725 Ω(5.1).Should(BeEquivalentTo(5)) 726 Ω(5).ShouldNot(BeEquivalentTo(5.1)) 727 ``` 728 729 the first assertion passes because 5.1 will be cast to an integer and will get rounded down! Such false positives are terrible and should be avoided. Use [`BeNumerically()`](#benumericallycomparator-string-compareto-interface) to compare numbers instead. 730 731 #### BeIdenticalTo(expected interface{}) 732 733 ```go 734 Ω(ACTUAL).Should(BeIdenticalTo(EXPECTED)) 735 ``` 736 737 Like `Equal`, `BeIdenticalTo` compares `ACTUAL` to `EXPECTED` for equality. Unlike `Equal`, however, it uses `==` to compare values. In practice, this means that primitive values like strings, integers and floats are identical to, as well as pointers to values. 738 739 `BeIdenticalTo` is most useful when you want to assert that two pointers point to the exact same location in memory. 740 741 As with `Equal` it is an error for both `ACTUAL` and `EXPECTED` to be nil, you should use `BeNil()` instead. 742 743 #### BeAssignableToTypeOf(expected interface) 744 745 ```go 746 Ω(ACTUAL).Should(BeAssignableToTypeOf(EXPECTED interface)) 747 ``` 748 749 succeeds if `ACTUAL` is a type that can be assigned to a variable with the same type as `EXPECTED`. It is an error for either `ACTUAL` or `EXPECTED` to be `nil`. 750 751 ### Asserting Presence 752 753 #### BeNil() 754 755 ```go 756 Ω(ACTUAL).Should(BeNil()) 757 ``` 758 759 succeeds if `ACTUAL` is, in fact, `nil`. 760 761 #### BeZero() 762 763 ```go 764 Ω(ACTUAL).Should(BeZero()) 765 ``` 766 767 succeeds if `ACTUAL` is the zero value for its type *or* if `ACTUAL` is `nil`. 768 769 ### Asserting Truthiness 770 771 #### BeTrue() 772 773 ```go 774 Ω(ACTUAL).Should(BeTrue()) 775 ``` 776 777 succeeds if `ACTUAL` is `bool` typed and has the value `true`. It is an error for `ACTUAL` to not be a `bool`. 778 779 Since Gomega has no additional context about your assertion the failure messages are generally not particularly helpful. So it's generally recommended that you use `BeTrueBecause` instead. 780 781 > Some matcher libraries have a notion of "truthiness" to assert that an object is present. Gomega is strict, and `BeTrue()` only works with `bool`s. You can use `Ω(ACTUAL).ShouldNot(BeZero())` or `Ω(ACTUAL).ShouldNot(BeNil())` to verify object presence. 782 783 ### BeTrueBecause(reason) 784 785 ```go 786 Ω(ACTUAL).Should(BeTrueBecause(REASON, ARGS...)) 787 ``` 788 789 is just like `BeTrue()` but allows you to pass in a reason. This is a best practice as the default failure message is not particularly helpful. `fmt.Sprintf(REASON, ARGS...)` is used to render the reason. For example: 790 791 ```go 792 Ω(cow.JumpedOver(moon)).Should(BeTrueBecause("the cow should have jumped over the moon")) 793 ``` 794 795 #### BeFalse() 796 797 ```go 798 Ω(ACTUAL).Should(BeFalse()) 799 ``` 800 801 succeeds if `ACTUAL` is `bool` typed and has the value `false`. It is an error for `ACTUAL` to not be a `bool`. You should generaly use `BeFalseBecause` instead to pas in a reason for a more helpful error message. 802 803 ### BeFalseBecause(reason) 804 805 ```go 806 Ω(ACTUAL).Should(BeFalseBecause(REASON, ARGS...)) 807 ``` 808 809 is just like `BeFalse()` but allows you to pass in a reason. This is a best practice as the default failure message is not particularly helpful. `fmt.Sprintf(REASON, ARGS...)` is used to render the reason. 810 811 ```go 812 Ω(cow.JumpedOver(mars)).Should(BeFalseBecause("the cow should not have jumped over mars")) 813 ``` 814 815 ### Asserting on Errors 816 817 #### HaveOccurred() 818 819 ```go 820 Ω(ACTUAL).Should(HaveOccurred()) 821 ``` 822 823 succeeds if `ACTUAL` is a non-nil `error`. Thus, the typical Go error checking pattern looks like: 824 825 ```go 826 err := SomethingThatMightFail() 827 Ω(err).ShouldNot(HaveOccurred()) 828 ``` 829 830 #### Succeed() 831 832 ```go 833 Ω(ACTUAL).Should(Succeed()) 834 ``` 835 836 succeeds if `ACTUAL` is `nil`. The intended usage is 837 838 ```go 839 Ω(FUNCTION()).Should(Succeed()) 840 ``` 841 842 where `FUNCTION()` is a function call that returns an error-type as its *first or only* return value. See [Handling Errors](#handling-errors) for a more detailed discussion. 843 844 #### MatchError(expected interface{}) 845 846 ```go 847 Ω(ACTUAL).Should(MatchError(EXPECTED, <FUNCTION_ERROR_DESCRIPTION>)) 848 ``` 849 850 succeeds if `ACTUAL` is a non-nil `error` that matches `EXPECTED`. `EXPECTED` must be one of the following: 851 852 - A string, in which case the matcher asserts that `ACTUAL.Error() == EXPECTED` 853 - An error (i.e. anything satisfying Go's `error` interface). In which case the matcher: 854 - First checks if `errors.Is(ACTUAL, EXPECTED)` returns `true` 855 - If not, it checks if `ACTUAL` or any of the errors it wraps (directly or indirectly) equals `EXPECTED` via `reflect.DeepEqual()`. 856 - A matcher, in which case `ACTUAL.Error()` is tested against the matcher, for example `Expect(err).Should(MatchError(ContainSubstring("sprocket not found")))` will pass if `err.Error()` has the substring "sprocke tnot found" 857 - A function with signature `func(error) bool`. The matcher then passes if `f(ACTUAL)` returns `true`. If using a function in this way you are required to pass a `FUNCTION_ERROR_DESCRIPTION` argument to `MatchError` that describes the function. This description is used in the failure message. For example: `Expect(err).To(MatchError(os.IsNotExist, "IsNotExist))` 858 859 Any other type for `EXPECTED` is an error. It is also an error for `ACTUAL` to be nil. Note that `FUNCTION_ERROR_DESCRIPTION` is a description of the error function, if used. This is required when passing a function but is ignored in all other cases. 860 861 ### Working with Channels 862 863 #### BeClosed() 864 865 ```go 866 Ω(ACTUAL).Should(BeClosed()) 867 ``` 868 869 succeeds if `ACTUAL` is a closed channel. It is an error to pass a non-channel to `BeClosed`, it is also an error to pass `nil`. 870 871 In order to check whether or not the channel is closed, Gomega must try to read from the channel (even in the `ShouldNot(BeClosed())` case). You should keep this in mind if you wish to make subsequent assertions about values coming down the channel. 872 873 Also, if you are testing that a *buffered* channel is closed you must first read all values out of the channel before asserting that it is closed (it is not possible to detect that a buffered-channel has been closed until all its buffered values are read). 874 875 Finally, as a corollary: it is an error to check whether or not a send-only channel is closed. 876 877 #### Receive() 878 879 ```go 880 Ω(ACTUAL).Should(Receive(<optionalPointer>)) 881 ``` 882 883 succeeds if there is a message to be received on actual. Actual must be a channel (and cannot be a send-only channel) -- anything else is an error. 884 885 `Receive` returns *immediately*. It *never* blocks: 886 887 - If there is nothing on the channel `c` then `Ω(c).Should(Receive())` will fail and `Ω(c).ShouldNot(Receive())` will pass. 888 - If there is something on the channel `c` ready to be read, then `Ω(c).Should(Receive())` will pass and `Ω(c).ShouldNot(Receive())` will fail. 889 - If the channel `c` is closed then `Ω(c).Should(Receive())` will fail and `Ω(c).ShouldNot(Receive())` will pass. 890 891 If you have a go-routine running in the background that will write to channel `c`, for example: 892 893 ```go 894 go func() { 895 time.Sleep(100 * time.Millisecond) 896 c <- true 897 }() 898 ``` 899 900 you can assert that `c` receives something (anything!) eventually: 901 902 ```go 903 Eventually(c).Should(Receive()) 904 ``` 905 906 This will timeout if nothing gets sent to `c` (you can modify the timeout interval as you normally do with `Eventually`). 907 908 A similar use-case is to assert that no go-routine writes to a channel (for a period of time). You can do this with `Consistently`: 909 910 ```go 911 Consistently(c).ShouldNot(Receive()) 912 ``` 913 914 `Receive` also allows you to make assertions on the received object. You do this by passing `Receive` a matcher: 915 916 ```go 917 Eventually(c).Should(Receive(Equal("foo"))) 918 ``` 919 920 This assertion will only succeed if `c` receives an object *and* that object satisfies `Equal("foo")`. Note that `Eventually` will continually poll `c` until this condition is met. If there are objects coming down the channel that do not satisfy the passed in matcher, they will be pulled off and discarded until an object that *does* satisfy the matcher is received. 921 922 In addition, there are occasions when you need to grab the object sent down the channel (e.g. to make several assertions against the object). To do this, you can ask the `Receive` matcher for the value passed to the channel by passing it a pointer to a variable of the appropriate type: 923 924 ```go 925 var receivedBagel Bagel 926 Eventually(bagelChan).Should(Receive(&receivedBagel)) 927 Ω(receivedBagel.Contents()).Should(ContainElement("cream cheese")) 928 Ω(receivedBagel.Kind()).Should(Equal("sesame")) 929 ``` 930 931 Of course, this could have been written as `receivedBagel := <-bagelChan` - however using `Receive` makes it easy to avoid hanging the test suite should nothing ever come down the channel. The pointer can point to any variable whose type is assignable from the channel element type, or if the channel type is an interface and the underlying type is assignable to the pointer. 932 933 Finally, `Receive` *never* blocks. `Eventually(c).Should(Receive())` repeatedly polls `c` in a non-blocking fashion. That means that you cannot use this pattern to verify that a *non-blocking send* has occurred on the channel - [more details at this GitHub issue](https://github.com/onsi/gomega/issues/82). 934 935 #### BeSent(value interface{}) 936 937 ```go 938 Ω(ACTUAL).Should(BeSent(VALUE)) 939 ``` 940 941 attempts to send `VALUE` to the channel `ACTUAL` without blocking. It succeeds if this is possible. 942 943 `ACTUAL` must be a channel (and cannot be a receive-only channel) that can be sent the type of the `VALUE` passed into `BeSent` -- anything else is an error. In addition, `ACTUAL` must not be closed. 944 945 `BeSent` never blocks: 946 947 - If the channel `c` is not ready to receive then `Ω(c).Should(BeSent("foo"))` will fail immediately. 948 - If the channel `c` is eventually ready to receive then `Eventually(c).Should(BeSent("foo"))` will succeed... presuming the channel becomes ready to receive before `Eventually`'s timeout. 949 - If the channel `c` is closed then `Ω(c).Should(BeSent("foo"))` and `Ω(c).ShouldNot(BeSent("foo"))` will both fail immediately. 950 951 Of course, `VALUE` is actually sent to the channel. The point of `BeSent` is less to make an assertion about the availability of the channel (which is typically an implementation detail that your test should not be concerned with). Rather, the point of `BeSent` is to make it possible to easily and expressively write tests that can timeout on blocked channel sends. 952 953 ### Working with files 954 955 #### BeAnExistingFile 956 957 ```go 958 Ω(ACTUAL).Should(BeAnExistingFile()) 959 ``` 960 961 succeeds if a file located at `ACTUAL` exists. 962 963 `ACTUAL` must be a string representing the filepath. 964 965 #### BeARegularFile 966 967 ```go 968 Ω(ACTUAL).Should(BeARegularFile()) 969 ``` 970 971 succeeds IFF a file located at `ACTUAL` exists and is a regular file. 972 973 `ACTUAL` must be a string representing the filepath. 974 975 #### BeADirectory 976 977 ```go 978 Ω(ACTUAL).Should(BeADirectory()) 979 ``` 980 981 succeeds IFF a file is located at `ACTUAL` exists and is a directory. 982 983 `ACTUAL` must be a string representing the filepath. 984 985 ### Working with Strings, JSON and YAML 986 987 #### ContainSubstring(substr string, args ...interface{}) 988 989 ```go 990 Ω(ACTUAL).Should(ContainSubstring(STRING, ARGS...)) 991 ``` 992 993 succeeds if `ACTUAL` contains the substring generated by: 994 995 ```go 996 fmt.Sprintf(STRING, ARGS...) 997 ``` 998 999 `ACTUAL` must either be a `string`, `[]byte` or a `Stringer` (a type implementing the `String()` method). Any other input is an error. 1000 1001 > Note, of course, that the `ARGS...` are not required. They are simply a convenience to allow you to build up strings programmatically inline in the matcher. 1002 1003 #### HavePrefix(prefix string, args ...interface{}) 1004 1005 ```go 1006 Ω(ACTUAL).Should(HavePrefix(STRING, ARGS...)) 1007 ``` 1008 1009 succeeds if `ACTUAL` has the string prefix generated by: 1010 1011 ```go 1012 fmt.Sprintf(STRING, ARGS...) 1013 ``` 1014 1015 `ACTUAL` must either be a `string`, `[]byte` or a `Stringer` (a type implementing the `String()` method). Any other input is an error. 1016 1017 > Note, of course, that the `ARGS...` are not required. They are simply a convenience to allow you to build up strings programmatically inline in the matcher. 1018 1019 #### HaveSuffix(suffix string, args ...interface{}) 1020 1021 ```go 1022 Ω(ACTUAL).Should(HaveSuffix(STRING, ARGS...)) 1023 ``` 1024 1025 succeeds if `ACTUAL` has the string suffix generated by: 1026 1027 ```go 1028 fmt.Sprintf(STRING, ARGS...) 1029 ``` 1030 1031 `ACTUAL` must either be a `string`, `[]byte` or a `Stringer` (a type implementing the `String()` method). Any other input is an error. 1032 1033 > Note, of course, that the `ARGS...` are not required. They are simply a convenience to allow you to build up strings programmatically inline in the matcher. 1034 1035 #### MatchRegexp(regexp string, args ...interface{}) 1036 1037 ```go 1038 Ω(ACTUAL).Should(MatchRegexp(STRING, ARGS...)) 1039 ``` 1040 1041 succeeds if `ACTUAL` is matched by the regular expression string generated by: 1042 1043 ```go 1044 fmt.Sprintf(STRING, ARGS...) 1045 ``` 1046 1047 `ACTUAL` must either be a `string`, `[]byte` or a `Stringer` (a type implementing the `String()` method). Any other input is an error. It is also an error for the regular expression to fail to compile. 1048 1049 > Note, of course, that the `ARGS...` are not required. They are simply a convenience to allow you to build up strings programmatically inline in the matcher. 1050 1051 #### MatchJSON(json interface{}) 1052 1053 ```go 1054 Ω(ACTUAL).Should(MatchJSON(EXPECTED)) 1055 ``` 1056 1057 Both `ACTUAL` and `EXPECTED` must be a `string`, `[]byte` or a `Stringer`. `MatchJSON` succeeds if both `ACTUAL` and `EXPECTED` are JSON representations of the same object. This is verified by parsing both `ACTUAL` and `EXPECTED` and then asserting equality on the resulting objects with `reflect.DeepEqual`. By doing this `MatchJSON` avoids any issues related to white space, formatting, and key-ordering. 1058 1059 It is an error for either `ACTUAL` or `EXPECTED` to be invalid JSON. 1060 1061 In some cases it is useful to match two JSON strings while ignoring list order. For this you can use the community maintained [MatchUnorderedJSON](https://github.com/Benjamintf1/Expanded-Unmarshalled-Matchers) matcher. 1062 1063 #### MatchXML(xml interface{}) 1064 1065 ```go 1066 Ω(ACTUAL).Should(MatchXML(EXPECTED)) 1067 ``` 1068 1069 Both `ACTUAL` and `EXPECTED` must be a `string`, `[]byte` or a `Stringer`. `MatchXML` succeeds if both `ACTUAL` and `EXPECTED` are XML representations of the same object. This is verified by parsing both `ACTUAL` and `EXPECTED` and then asserting equality on the resulting objects with `reflect.DeepEqual`. By doing this `MatchXML` avoids any issues related to white space or formatting. 1070 1071 It is an error for either `ACTUAL` or `EXPECTED` to be invalid XML. 1072 1073 #### MatchYAML(yaml interface{}) 1074 1075 ```go 1076 Ω(ACTUAL).Should(MatchYAML(EXPECTED)) 1077 ``` 1078 1079 Both `ACTUAL` and `EXPECTED` must be a `string`, `[]byte` or a `Stringer`. `MatchYAML` succeeds if both `ACTUAL` and `EXPECTED` are YAML representations of the same object. This is verified by parsing both `ACTUAL` and `EXPECTED` and then asserting equality on the resulting objects with `reflect.DeepEqual`. By doing this `MatchYAML` avoids any issues related to white space, formatting, and key-ordering. 1080 1081 It is an error for either `ACTUAL` or `EXPECTED` to be invalid YAML. 1082 1083 ### Working with Collections 1084 1085 #### BeEmpty() 1086 1087 ```go 1088 Ω(ACTUAL).Should(BeEmpty()) 1089 ``` 1090 1091 succeeds if `ACTUAL` is, in fact, empty. `ACTUAL` must be of type `string`, `array`, `map`, `chan`, or `slice`. It is an error for it to have any other type. 1092 1093 #### HaveLen(count int) 1094 1095 ```go 1096 Ω(ACTUAL).Should(HaveLen(INT)) 1097 ``` 1098 1099 succeeds if the length of `ACTUAL` is `INT`. `ACTUAL` must be of type `string`, `array`, `map`, `chan`, or `slice`. It is an error for it to have any other type. 1100 1101 #### HaveCap(count int) 1102 1103 ```go 1104 Ω(ACTUAL).Should(HaveCap(INT)) 1105 ``` 1106 1107 succeeds if the capacity of `ACTUAL` is `INT`. `ACTUAL` must be of type `array`, `chan`, or `slice`. It is an error for it to have any other type. 1108 1109 #### ContainElement(element interface{}) 1110 1111 ```go 1112 Ω(ACTUAL).Should(ContainElement(ELEMENT)) 1113 ``` 1114 1115 or 1116 1117 ```go 1118 Ω(ACTUAL).Should(ContainElement(ELEMENT, <POINTER>)) 1119 ``` 1120 1121 1122 succeeds if `ACTUAL` contains an element that equals `ELEMENT`. `ACTUAL` must be an `array`, `slice`, or `map` -- anything else is an error. For `map`s `ContainElement` searches through the map's values (not keys!). 1123 1124 By default `ContainElement()` uses the `Equal()` matcher under the hood to assert equality between `ACTUAL`'s elements and `ELEMENT`. You can change this, however, by passing `ContainElement` a `GomegaMatcher`. For example, to check that a slice of strings has an element that matches a substring: 1125 1126 ```go 1127 Ω([]string{"Foo", "FooBar"}).Should(ContainElement(ContainSubstring("Bar"))) 1128 ``` 1129 1130 In addition, there are occasions when you need to grab (all) matching contained elements, for instance, to make several assertions against the matching contained elements. To do this, you can ask the `ContainElement()` matcher for the matching contained elements by passing it a pointer to a variable of the appropriate type. If multiple matching contained elements are expected, then a pointer to either a slice or a map should be passed (but not a pointer to an array), otherwise a pointer to a scalar (non-slice, non-map): 1131 1132 ```go 1133 var findings []string 1134 Ω([]string{"foo", "foobar", "bar"}).Should(ContainElement(ContainSubstring("foo"), &findings)) 1135 1136 var finding string 1137 Ω([]string{"foo", "foobar", "bar"}).Should(ContainElement("foobar", &finding)) 1138 ``` 1139 1140 The `ContainElement` matcher will fail with a descriptive error message in case of multiple matches when the pointer references a scalar type. 1141 1142 In case of maps, the matching contained elements will be returned with their keys in the map referenced by the pointer. 1143 1144 ```go 1145 var findings map[int]string 1146 Ω(map[int]string{ 1147 1: "bar", 1148 2: "foobar", 1149 3: "foo", 1150 }).Should(ContainElement(ContainSubstring("foo"), &findings)) 1151 ``` 1152 1153 #### ContainElements(element ...interface{}) 1154 1155 ```go 1156 Ω(ACTUAL).Should(ContainElements(ELEMENT1, ELEMENT2, ELEMENT3, ...)) 1157 ``` 1158 1159 or 1160 1161 ```go 1162 Ω(ACTUAL).Should(ContainElements([]SOME_TYPE{ELEMENT1, ELEMENT2, ELEMENT3, ...})) 1163 ``` 1164 1165 succeeds if `ACTUAL` contains the elements passed into the matcher. The ordering of the elements does not matter. 1166 1167 By default `ContainElements()` uses `Equal()` to match the elements, however custom matchers can be passed in instead. Here are some examples: 1168 1169 ```go 1170 Ω([]string{"Foo", "FooBar"}).Should(ContainElements("FooBar")) 1171 Ω([]string{"Foo", "FooBar"}).Should(ContainElements(ContainSubstring("Bar"), "Foo")) 1172 ``` 1173 1174 Actual must be an `array`, `slice` or `map`. For maps, `ContainElements` matches against the `map`'s values. 1175 1176 You typically pass variadic arguments to `ContainElements` (as in the examples above). However, if you need to pass in a slice you can provided that it 1177 is the only element passed in to `ContainElements`: 1178 1179 ```go 1180 Ω([]string{"Foo", "FooBar"}).Should(ContainElements([]string{"FooBar", "Foo"})) 1181 ``` 1182 1183 Note that Go's type system does not allow you to write this as `ContainElements([]string{"FooBar", "Foo"}...)` as `[]string` and `[]interface{}` are different types - hence the need for this special rule. 1184 1185 The difference between the `ContainElements` and `ConsistOf` matchers is that the latter is more restrictive because the `ConsistOf` matcher checks additionally that the `ACTUAL` elements and the elements passed into the matcher have the same length. 1186 1187 #### BeElementOf(elements ...interface{}) 1188 1189 ```go 1190 Ω(ACTUAL).Should(BeElementOf(ELEMENT1, ELEMENT2, ELEMENT3, ...)) 1191 ``` 1192 1193 succeeds if `ACTUAL` equals one of the elements passed into the matcher. When a single element `ELEMENT` of type `array` or `slice` is passed into the matcher, `BeElementOf` succeeds if `ELEMENT` contains an element that equals `ACTUAL` (reverse of `ContainElement`). `BeElementOf` always uses the `Equal()` matcher under the hood to assert equality. 1194 1195 #### BeKeyOf(m interface{}) 1196 1197 ```go 1198 Ω(ACTUAL).Should(BeKeyOf(MAP)) 1199 ``` 1200 1201 succeeds if `ACTUAL` equals one of the keys of `MAP`. It is an error for `MAP` to be of any type other than a map. `BeKeyOf` always uses the `Equal()` matcher under the hood to assert equality of `ACTUAL` with a map key. 1202 1203 `BeKeyOf` can be used in situations where it is not possible to rewrite an assertion to use the more idiomatic `HaveKey`: one use is in combination with `ContainElement` doubling as a filter. For instance, the following example asserts that all expected specific sprockets are present in a larger list of sprockets: 1204 1205 ```go 1206 var names = map[string]struct { 1207 detail string 1208 }{ 1209 "edgy_emil": {detail: "sprocket_project_A"}, 1210 "furious_freddy": {detail: "sprocket_project_B"}, 1211 } 1212 1213 var canaries []Sprocket 1214 Expect(projects).To(ContainElement(HaveField("Name", BeKeyOf(names)), &canaries)) 1215 Expect(canaries).To(HaveLen(len(names))) 1216 ``` 1217 1218 #### ConsistOf(element ...interface{}) 1219 1220 ```go 1221 Ω(ACTUAL).Should(ConsistOf(ELEMENT1, ELEMENT2, ELEMENT3, ...)) 1222 ``` 1223 1224 or 1225 1226 ```go 1227 Ω(ACTUAL).Should(ConsistOf([]SOME_TYPE{ELEMENT1, ELEMENT2, ELEMENT3, ...})) 1228 ``` 1229 1230 succeeds if `ACTUAL` contains precisely the elements passed into the matcher. The ordering of the elements does not matter. 1231 1232 By default `ConsistOf()` uses `Equal()` to match the elements, however custom matchers can be passed in instead. Here are some examples: 1233 1234 ```go 1235 Ω([]string{"Foo", "FooBar"}).Should(ConsistOf("FooBar", "Foo")) 1236 Ω([]string{"Foo", "FooBar"}).Should(ConsistOf(ContainSubstring("Bar"), "Foo")) 1237 Ω([]string{"Foo", "FooBar"}).Should(ConsistOf(ContainSubstring("Foo"), ContainSubstring("Foo"))) 1238 ``` 1239 1240 Actual must be an `array`, `slice` or `map`. For maps, `ConsistOf` matches against the `map`'s values. 1241 1242 You typically pass variadic arguments to `ConsistOf` (as in the examples above). However, if you need to pass in a slice you can provided that it 1243 is the only element passed in to `ConsistOf`: 1244 1245 ```go 1246 Ω([]string{"Foo", "FooBar"}).Should(ConsistOf([]string{"FooBar", "Foo"})) 1247 ``` 1248 1249 Note that Go's type system does not allow you to write this as `ConsistOf([]string{"FooBar", "Foo"}...)` as `[]string` and `[]interface{}` are different types - hence the need for this special rule. 1250 1251 #### HaveExactElements(element ...interface{}) 1252 1253 ```go 1254 Expect(ACTUAL).To(HaveExactElements(ELEMENT1, ELEMENT2, ELEMENT3, ...)) 1255 ``` 1256 1257 or 1258 1259 ```go 1260 Expect(ACTUAL).To(HaveExactElements([]SOME_TYPE{ELEMENT1, ELEMENT2, ELEMENT3, ...})) 1261 ``` 1262 1263 succeeds if `ACTUAL` contains precisely the elements and ordering passed into the matchers. 1264 1265 By default `HaveExactElements()` uses `Equal()` to match the elements, however custom matchers can be passed in instead. Here are some examples: 1266 1267 ```go 1268 Expect([]string{"Foo", "FooBar"}).To(HaveExactElements("Foo", "FooBar")) 1269 Expect([]string{"Foo", "FooBar"}).To(HaveExactElements("Foo", ContainSubstring("Bar"))) 1270 Expect([]string{"Foo", "FooBar"}).To(HaveExactElements(ContainSubstring("Foo"), ContainSubstring("Foo"))) 1271 ``` 1272 1273 Actual must be an `array` or `slice`. 1274 1275 You typically pass variadic arguments to `HaveExactElements` (as in the examples above). However, if you need to pass in a slice you can provided that it 1276 is the only element passed in to `HaveExactElements`: 1277 1278 ```go 1279 Expect([]string{"Foo", "FooBar"}).To(HaveExactElements([]string{"FooBar", "Foo"})) 1280 ``` 1281 1282 Note that Go's type system does not allow you to write this as `HaveExactElements([]string{"FooBar", "Foo"}...)` as `[]string` and `[]interface{}` are different types - hence the need for this special rule. 1283 1284 #### HaveEach(element interface{}) 1285 1286 ```go 1287 Ω(ACTUAL).Should(HaveEach(ELEMENT)) 1288 ``` 1289 1290 succeeds if `ACTUAL` solely consists of elements that equal `ELEMENT`. `ACTUAL` must be an `array`, `slice`, or `map` -- anything else is an error. For `map`s `HaveEach` searches through the map's values (not keys!). 1291 1292 In order to avoid ambiguity it is an error for `ACTUAL` to be an empty `array`, `slice`, or `map` (or a correctly typed `nil`) -- in these cases it cannot be decided if `HaveEach` should match, or should not match. If in your test it is acceptable for `ACTUAL` to be empty, you can use `Or(BeEmpty(), HaveEach(ELEMENT))` instead. 1293 1294 By default `HaveEach()` uses the `Equal()` matcher under the hood to assert equality between `ACTUAL`'s elements and `ELEMENT`. You can change this, however, by passing `HaveEach` a `GomegaMatcher`. For example, to check that a slice of strings has an element that matches a substring: 1295 1296 ```go 1297 Ω([]string{"Foo", "FooBar"}).Should(HaveEach(ContainSubstring("Foo"))) 1298 ``` 1299 1300 #### HaveKey(key interface{}) 1301 1302 ```go 1303 Ω(ACTUAL).Should(HaveKey(KEY)) 1304 ``` 1305 1306 succeeds if `ACTUAL` is a map with a key that equals `KEY`. It is an error for `ACTUAL` to not be a `map`. 1307 1308 By default `HaveKey()` uses the `Equal()` matcher under the hood to assert equality between `ACTUAL`'s keys and `KEY`. You can change this, however, by passing `HaveKey` a `GomegaMatcher`. For example, to check that a map has a key that matches a regular expression: 1309 1310 ```go 1311 Ω(map[string]string{"Foo": "Bar", "BazFoo": "Duck"}).Should(HaveKey(MatchRegexp(`.+Foo$`))) 1312 ``` 1313 1314 #### HaveKeyWithValue(key interface{}, value interface{}) 1315 1316 ```go 1317 Ω(ACTUAL).Should(HaveKeyWithValue(KEY, VALUE)) 1318 ``` 1319 1320 succeeds if `ACTUAL` is a map with a key that equals `KEY` mapping to a value that equals `VALUE`. It is an error for `ACTUAL` to not be a `map`. 1321 1322 By default `HaveKeyWithValue()` uses the `Equal()` matcher under the hood to assert equality between `ACTUAL`'s keys and `KEY` and between the associated value and `VALUE`. You can change this, however, by passing `HaveKeyWithValue` a `GomegaMatcher` for either parameter. For example, to check that a map has a key that matches a regular expression and which is also associated with a value that passes some numerical threshold: 1323 1324 ```go 1325 Ω(map[string]int{"Foo": 3, "BazFoo": 4}).Should(HaveKeyWithValue(MatchRegexp(`.+Foo$`), BeNumerically(">", 3))) 1326 ``` 1327 1328 #### HaveField(field interface{}, value interface{}) 1329 1330 ```go 1331 Ω(ACTUAL).Should(HaveField(FIELD, VALUE)) 1332 ``` 1333 1334 succeeds if `ACTUAL` is a struct with a value that can be traversed via `FIELD` that equals `VALUE`. It is an error for `ACTUAL` to not be a `struct`. 1335 1336 By default `HaveField()` uses the `Equal()` matcher under the hood to assert equality between the extracted value and `VALUE`. You can change this, however, by passing `HaveField` a `GomegaMatcher` for `VALUE`. 1337 1338 `FIELD` allows you to access fields within the `ACTUAL` struct. Nested structs can be accessed using the `.` delimiter. `HaveField()` also allows you to invoke methods on the struct by adding a `()` suffix to the `FIELD` - these methods must take no arguments and return exactly one value. For example consider the following types: 1339 1340 ```go 1341 type Book struct { 1342 Title string 1343 Author Person 1344 } 1345 1346 type Person struct { 1347 Name string 1348 DOB time.Time 1349 } 1350 ``` 1351 1352 and an instance book `var book = Book{...}` - you can use `HaveField` to make assertions like: 1353 1354 ```go 1355 Ω(book).Should(HaveField("Title", "Les Miserables")) 1356 Ω(book).Should(HaveField("Title", ContainSubstring("Les Mis"))) 1357 Ω(book).Should(HaveField("Author.Name", "Victor Hugo")) 1358 Ω(book).Should(HaveField("Author.DOB.Year()", BeNumerically("<", 1900))) 1359 ``` 1360 1361 `HaveField` can pair powerfully with a collection matcher like `ContainElement`. To assert that a list of books as at least one element with an author born in February you could write: 1362 1363 ```go 1364 Ω(books).Should(ContainElement(HaveField("Author.DOB.Month()", Equal(2)))) 1365 ``` 1366 1367 If you want to make lots of complex assertions against the fields of a struct take a look at the [`gstruct`package](#codegstructcode-testing-complex-data-types) documented below. 1368 1369 #### HaveExistingField(field interface{}) 1370 1371 While `HaveField()` considers a missing field to be an error (instead of non-success), combining it with `HaveExistingField()` allows `HaveField()` to be reused in test contexts other than assertions: for instance, as filters to [`ContainElement(ELEMENT, <POINTER>)`](#containelementelement-interface) or in detecting resource leaks (like leaked file descriptors). 1372 1373 ```go 1374 Ω(ACTUAL).Should(HaveExistingField(FIELD)) 1375 ``` 1376 1377 succeeds if `ACTUAL` is a struct with a field `FIELD`, regardless of this field's value. It is an error for `ACTUAL` to not be a `struct`. Like `HaveField()`, `HaveExistingField()` supports accessing nested structs using the `.` delimiter. Methods on the struct are invoked by adding a `()` suffix to the `FIELD` - these methods must take no arguments and return exactly one value. 1378 1379 To assert a particular field value, but only if such a field exists in an `ACTUAL` struct, use the composing [`And`](#andmatchers-gomegamatcher) matcher: 1380 1381 ```go 1382 Ω(ACTUAL).Should(And(HaveExistingField(FIELD), HaveField(FIELD, VALUE))) 1383 ``` 1384 1385 ### Working with Numbers and Times 1386 1387 #### BeNumerically(comparator string, compareTo ...interface{}) 1388 1389 ```go 1390 Ω(ACTUAL).Should(BeNumerically(COMPARATOR_STRING, EXPECTED, <THRESHOLD>)) 1391 ``` 1392 1393 performs numerical assertions in a type-agnostic way. `ACTUAL` and `EXPECTED` should be numbers, though the specific type of number is irrelevant (`float32`, `float64`, `uint8`, etc...). It is an error for `ACTUAL` or `EXPECTED` to not be a number. 1394 1395 There are six supported comparators: 1396 1397 - `Ω(ACTUAL).Should(BeNumerically("==", EXPECTED))`: 1398 asserts that `ACTUAL` and `EXPECTED` are numerically equal. 1399 1400 - `Ω(ACTUAL).Should(BeNumerically("~", EXPECTED, <THRESHOLD>))`: 1401 asserts that `ACTUAL` and `EXPECTED` are within `<THRESHOLD>` of one another. By default `<THRESHOLD>` is `1e-8` but you can specify a custom value. 1402 1403 - `Ω(ACTUAL).Should(BeNumerically(">", EXPECTED))`: 1404 asserts that `ACTUAL` is greater than `EXPECTED`. 1405 1406 - `Ω(ACTUAL).Should(BeNumerically(">=", EXPECTED))`: 1407 asserts that `ACTUAL` is greater than or equal to `EXPECTED`. 1408 1409 - `Ω(ACTUAL).Should(BeNumerically("<", EXPECTED))`: 1410 asserts that `ACTUAL` is less than `EXPECTED`. 1411 1412 - `Ω(ACTUAL).Should(BeNumerically("<=", EXPECTED))`: 1413 asserts that `ACTUAL` is less than or equal to `EXPECTED`. 1414 1415 Any other comparator is an error. 1416 1417 #### BeTemporally(comparator string, compareTo time.Time, threshold ...time.Duration) 1418 1419 ```go 1420 Ω(ACTUAL).Should(BeTemporally(COMPARATOR_STRING, EXPECTED_TIME, <THRESHOLD_DURATION>)) 1421 ``` 1422 1423 performs time-related assertions. `ACTUAL` must be a `time.Time`. 1424 1425 There are six supported comparators: 1426 1427 - `Ω(ACTUAL).Should(BeTemporally("==", EXPECTED_TIME))`: 1428 asserts that `ACTUAL` and `EXPECTED_TIME` are identical `time.Time`s. 1429 1430 - `Ω(ACTUAL).Should(BeTemporally("~", EXPECTED_TIME, <THRESHOLD_DURATION>))`: 1431 asserts that `ACTUAL` and `EXPECTED_TIME` are within `<THRESHOLD_DURATION>` of one another. By default `<THRESHOLD_DURATION>` is `time.Millisecond` but you can specify a custom value. 1432 1433 - `Ω(ACTUAL).Should(BeTemporally(">", EXPECTED_TIME))`: 1434 asserts that `ACTUAL` is after `EXPECTED_TIME`. 1435 1436 - `Ω(ACTUAL).Should(BeTemporally(">=", EXPECTED_TIME))`: 1437 asserts that `ACTUAL` is after or at `EXPECTED_TIME`. 1438 1439 - `Ω(ACTUAL).Should(BeTemporally("<", EXPECTED_TIME))`: 1440 asserts that `ACTUAL` is before `EXPECTED_TIME`. 1441 1442 - `Ω(ACTUAL).Should(BeTemporally("<=", EXPECTED_TIME))`: 1443 asserts that `ACTUAL` is before or at `EXPECTED_TIME`. 1444 1445 Any other comparator is an error. 1446 1447 ### Working with Values 1448 1449 #### HaveValue(matcher types.GomegaMatcher) 1450 1451 `HaveValue` applies `MATCHER` to the value that results from dereferencing `ACTUAL` in case of a pointer or an interface, or otherwise `ACTUAL` itself. Pointers and interfaces are dereferenced multiple times as necessary, with a limit of at most 31 dereferences. It will fail if the pointer value is `nil`: 1452 1453 ```go 1454 Expect(ACTUAL).To(HaveValue(MATCHER)) 1455 ``` 1456 1457 For instance: 1458 1459 ```go 1460 i := 42 1461 Expect(&i).To(HaveValue(Equal(42))) 1462 Expect(i).To(HaveValue(Equal(42))) 1463 ``` 1464 1465 `HaveValue` can be used, for instance, in tests and custom matchers where the it doesn't matter (as opposed to `PointTo`) if a value first needs to be dereferenced or not. This is especially useful to custom matchers that are to be used in mixed contexts of pointers as well as non-pointers. 1466 1467 Please note that negating the outcome of `HaveValue(nil)` won't suppress any error; for instance, in order to assert not having a specific value while still accepting `nil` the following matcher expression might be used: 1468 1469 ```go 1470 Or(BeNil(), Not(HaveValue(...))) 1471 ``` 1472 1473 ### Working with HTTP responses 1474 1475 #### HaveHTTPStatus(expected interface{}) 1476 1477 ```go 1478 Expect(ACTUAL).To(HaveHTTPStatus(EXPECTED, ...)) 1479 ``` 1480 1481 succeeds if the `Status` or `StatusCode` field of an HTTP response matches. 1482 1483 `ACTUAL` must be either a `*http.Response` or `*httptest.ResponseRecorder`. 1484 1485 `EXPECTED` must be one or more `int` or `string` types. An `int` is compared 1486 to `StatusCode` and a `string` is compared to `Status`. 1487 The matcher succeeds if any of the `EXPECTED` values match. 1488 1489 Here are some examples: 1490 1491 - `Expect(resp).To(HaveHTTPStatus(http.StatusOK, http.StatusNoContext))`: 1492 asserts that `resp.StatusCode == 200` or `resp.StatusCode == 204` 1493 1494 - `Expect(resp).To(HaveHTTPStatus("404 Not Found"))`: 1495 asserts that `resp.Status == "404 Not Found"`. 1496 1497 #### HaveHTTPBody(expected interface{}) 1498 1499 ```go 1500 Expect(ACTUAL).To(HaveHTTPBody(EXPECTED)) 1501 ``` 1502 1503 Succeeds if the body of an HTTP Response matches. 1504 1505 `ACTUAL` must be either a `*http.Response` or `*httptest.ResponseRecorder`. 1506 1507 `EXPECTED` must be one of the following: 1508 - A `string` 1509 - A `[]byte` 1510 - A matcher, in which case the matcher will be called with the body as a `[]byte`. 1511 1512 Here are some examples: 1513 1514 - `Expect(resp).To(HaveHTTPBody("bar"))`: 1515 asserts that when `resp.Body` is read, it will equal `bar`. 1516 1517 - `Expect(resp).To(HaveHTTPBody(MatchJSON("{\"some\":\"json\""))`: 1518 asserts that when `resp.Body` is read, the `MatchJSON` matches it to `{"some":"json"}`. 1519 1520 Note that the body is an `io.ReadCloser` and the `HaveHTTPBody()` will read it and the close it. 1521 This means that subsequent attempts to read the body may have unexpected results. 1522 1523 #### HaveHTTPHeaderWithValue(key string, value interface{}) 1524 1525 ```go 1526 Expect(ACTUAL).To(HaveHTTPHeaderWithValue(KEY, VALUE)) 1527 ``` 1528 1529 Succeeds if the HTTP Response has a matching header and value. 1530 1531 `ACTUAL` must be either a `*http.Response` or `*httptest.ResponseRecorder`. 1532 1533 `KEY` must be a `string`. It is passed to 1534 [`http.Header.Get(key string)`](https://pkg.go.dev/net/http#Header.Get), 1535 and will have the same behaviors regarding order of headers and capitalization. 1536 1537 `VALUE` must be one of the following: 1538 - A `string` 1539 - A matcher, in which case the matcher will be called to match the value. 1540 1541 Here are some examples: 1542 1543 - `Expect(resp).To(HaveHTTPHeaderWithValue("Content-Type", "application/json"))`: 1544 asserts that the `Content-Type` header has exactly the value `application/json`. 1545 1546 - `Expect(resp).To(HaveHTTPHeaderWithValue(ContainSubstring("json")))`: 1547 asserts that the `Content-Type` header contains the substring `json`. 1548 1549 ### Asserting on Panics 1550 1551 #### Panic() 1552 1553 ```go 1554 Ω(ACTUAL).Should(Panic()) 1555 ``` 1556 1557 succeeds if `ACTUAL` is a function that, when invoked, panics. `ACTUAL` must be a function that takes no arguments and returns no result -- any other type for `ACTUAL` is an error. 1558 1559 #### PanicWith() 1560 1561 ```go 1562 Ω(ACTUAL).Should(PanicWith(VALUE)) 1563 ``` 1564 1565 succeeds if `ACTUAL` is a function that, when invoked, panics with a value of `VALUE`. `ACTUAL` must be a function that takes no arguments and returns no result -- any other type for `ACTUAL` is an error. 1566 1567 By default `PanicWith()` uses the `Equal()` matcher under the hood to assert equality between `ACTUAL`'s panic value and `VALUE`. You can change this, however, by passing `PanicWith` a `GomegaMatcher`. For example, to check that the panic value matches a regular expression: 1568 1569 ```go 1570 Ω(func() { panic("FooBarBaz") }).Should(PanicWith(MatchRegexp(`.+Baz$`))) 1571 ``` 1572 1573 ### Composing Matchers 1574 1575 You may form larger matcher expressions using the following operators: `And()`, `Or()`, `Not()` and `WithTransform()`. 1576 1577 Note: `And()` and `Or()` can also be referred to as `SatisfyAll()` and `SatisfyAny()`, respectively. 1578 1579 With these operators you can express multiple requirements in a single `Expect()` or `Eventually()` statement. For example: 1580 1581 ```go 1582 Expect(number).To(SatisfyAll( 1583 BeNumerically(">", 0), 1584 BeNumerically("<", 10))) 1585 1586 Expect(msg).To(SatisfyAny( 1587 Equal("Success"), 1588 MatchRegexp(`^Error .+$`))) 1589 ``` 1590 1591 It can also provide a lightweight syntax to create new matcher types from existing ones. For example: 1592 1593 ```go 1594 func BeBetween(min, max int) GomegaMatcher { 1595 return SatisfyAll( 1596 BeNumerically(">", min), 1597 BeNumerically("<", max)) 1598 } 1599 1600 Ω(number).Should(BeBetween(0, 10)) 1601 ``` 1602 1603 #### And(matchers ...GomegaMatcher) 1604 1605 #### SatisfyAll(matchers ...GomegaMatcher) 1606 1607 ```go 1608 Ω(ACTUAL).Should(And(MATCHER1, MATCHER2, ...)) 1609 ``` 1610 1611 or 1612 1613 ```go 1614 Ω(ACTUAL).Should(SatisfyAll(MATCHER1, MATCHER2, ...)) 1615 ``` 1616 1617 succeeds if `ACTUAL` satisfies all of the specified matchers (similar to a logical AND). 1618 1619 Tests the given matchers in order, returning immediately if one fails, without needing to test the remaining matchers. 1620 1621 #### Or(matchers ...GomegaMatcher) 1622 1623 #### SatisfyAny(matchers ...GomegaMatcher) 1624 1625 ```go 1626 Ω(ACTUAL).Should(Or(MATCHER1, MATCHER2, ...)) 1627 ``` 1628 1629 or 1630 1631 ```go 1632 Ω(ACTUAL).Should(SatisfyAny(MATCHER1, MATCHER2, ...)) 1633 ``` 1634 1635 succeeds if `ACTUAL` satisfies any of the specified matchers (similar to a logical OR). 1636 1637 Tests the given matchers in order, returning immediately if one succeeds, without needing to test the remaining matchers. 1638 1639 #### Not(matcher GomegaMatcher) 1640 1641 ```go 1642 Ω(ACTUAL).Should(Not(MATCHER)) 1643 ``` 1644 1645 succeeds if `ACTUAL` does **not** satisfy the specified matcher (similar to a logical NOT). 1646 1647 #### WithTransform(transform interface{}, matcher GomegaMatcher) 1648 1649 ```go 1650 Ω(ACTUAL).Should(WithTransform(TRANSFORM, MATCHER)) 1651 ``` 1652 1653 succeeds if applying the `TRANSFORM` function to `ACTUAL` (i.e. the value of `TRANSFORM(ACTUAL)`) will satisfy the given `MATCHER`. For example: 1654 1655 ```go 1656 GetColor := func(e Element) Color { return e.Color } 1657 1658 Ω(element).Should(WithTransform(GetColor, Equal(BLUE))) 1659 ``` 1660 1661 Or the same thing expressed by introducing a new, lightweight matcher: 1662 1663 ```go 1664 // HaveColor returns a matcher that expects the element to have the given color. 1665 func HaveColor(c Color) GomegaMatcher { 1666 return WithTransform(func(e Element) Color { 1667 return e.Color 1668 }, Equal(c)) 1669 } 1670 1671 Ω(element).Should(HaveColor(BLUE))) 1672 ``` 1673 1674 `TRANSFORM` functions optionally can return an additional error value in case a transformation is not possible, avoiding the need to `panic`. Returning errors can be useful when using `WithTransform` to build lightweight matchers that accept different value types and that can gracefully fail when presented the wrong value type. 1675 1676 As before, such a `TRANSFORM` expects a single actual value. But now it returns the transformed value together with an error value. This follows the common Go idiom to communicate errors via an explicit, separate return value. 1677 1678 The following lightweight matcher expects to be used either on a `Sprocket` value or `*Sprocket` pointer. It gracefully fails when the actual value is something else. 1679 1680 ```go 1681 // HaveSprocketName returns a matcher that expects the actual value to be 1682 // either a Sprocket or a *Sprocket, having the specified name. 1683 func HaveSprocketName(name string) GomegaMatcher { 1684 return WithTransform( 1685 func(actual interface{}) (string, error) { 1686 switch sprocket := actual.(type) { 1687 case *Sprocket: 1688 return Sprocket.Name, nil 1689 case Sprocket: 1690 return Sprocket.Name, nil 1691 default: 1692 return "", fmt.Errorf("HaveSprocketName expects a Sprocket or *Sprocket, but got %T", actual) 1693 } 1694 }, Equal(name)) 1695 } 1696 1697 Ω(element).Should(HaveSprocketName("gomega"))) 1698 ``` 1699 1700 #### Satisfy(predicate interface{}) 1701 1702 ```go 1703 Ω(ACTUAL).Should(Satisfy(PREDICATE)) 1704 ``` 1705 1706 succeeds if applying the `PREDICATE` function to `ACTUAL` (i.e. the value of `PREDICATE(ACTUAL)`) will return `true`. For example: 1707 1708 ```go 1709 IsEven := func(i int) bool { return i%2==0 } 1710 1711 Ω(number).Should(Satisfy(IsEven)) 1712 ``` 1713 1714 ## Adding Your Own Matchers 1715 1716 A matcher, in Gomega, is any type that satisfies the `GomegaMatcher` interface: 1717 1718 ```go 1719 type GomegaMatcher interface { 1720 Match(actual interface{}) (success bool, err error) 1721 FailureMessage(actual interface{}) (message string) 1722 NegatedFailureMessage(actual interface{}) (message string) 1723 } 1724 ``` 1725 1726 For the simplest cases, new matchers can be [created by composition](#composing-matchers). Please also take a look at the [Building Custom Matchers](https://onsi.github.io/ginkgo/#building-custom-matchers) section of the Ginkgo and Gomega patterns chapter in the Ginkgo docs for additional examples. 1727 1728 In addition to composition, however, it is fairly straightforward to build domain-specific custom matchers. You can create new types that satisfy the `GomegaMatcher` interface *or* you can use the `gcustom` package to build matchers out of simple functions. 1729 1730 Let's work through an example and illustrate both approaches. 1731 1732 ### A Custom Matcher: RepresentJSONifiedObject(EXPECTED interface{}) 1733 1734 Say you're working on a JSON API and you want to assert that your server returns the correct JSON representation. Rather than marshal/unmarshal JSON in your tests, you want to write an expressive matcher that checks that the received response is a JSON representation for the object in question. This is what the `RepresentJSONifiedObject` matcher could look like: 1735 1736 ```go 1737 package json_response_matcher 1738 1739 import ( 1740 "github.com/onsi/gomega/types" 1741 1742 "encoding/json" 1743 "fmt" 1744 "net/http" 1745 "reflect" 1746 ) 1747 1748 func RepresentJSONifiedObject(expected interface{}) types.GomegaMatcher { 1749 return &representJSONMatcher{ 1750 expected: expected, 1751 } 1752 } 1753 1754 type representJSONMatcher struct { 1755 expected interface{} 1756 } 1757 1758 func (matcher *representJSONMatcher) Match(actual interface{}) (success bool, err error) { 1759 response, ok := actual.(*http.Response) 1760 if !ok { 1761 return false, fmt.Errorf("RepresentJSONifiedObject matcher expects an http.Response") 1762 } 1763 1764 pointerToObjectOfExpectedType := reflect.New(reflect.TypeOf(matcher.expected)).Interface() 1765 err = json.NewDecoder(response.Body).Decode(pointerToObjectOfExpectedType) 1766 1767 if err != nil { 1768 return false, fmt.Errorf("Failed to decode JSON: %s", err.Error()) 1769 } 1770 1771 decodedObject := reflect.ValueOf(pointerToObjectOfExpectedType).Elem().Interface() 1772 1773 return reflect.DeepEqual(decodedObject, matcher.expected), nil 1774 } 1775 1776 func (matcher *representJSONMatcher) FailureMessage(actual interface{}) (message string) { 1777 return fmt.Sprintf("Expected\n\t%#v\nto contain the JSON representation of\n\t%#v", actual, matcher.expected) 1778 } 1779 1780 func (matcher *representJSONMatcher) NegatedFailureMessage(actual interface{}) (message string) { 1781 return fmt.Sprintf("Expected\n\t%#v\nnot to contain the JSON representation of\n\t%#v", actual, matcher.expected) 1782 } 1783 ``` 1784 1785 Let's break this down: 1786 1787 - Most matchers have a constructor function that returns an instance of the matcher. In this case we've created `RepresentJSONifiedObject`. Where possible, your constructor function should take explicit types or interfaces. For our use case, however, we need to accept any possible expected type so `RepresentJSONifiedObject` takes an argument with the generic `interface{}` type. 1788 - The constructor function then initializes and returns an instance of our matcher: the `representJSONMatcher`. These rarely need to be exported outside of your matcher package. 1789 - The `representJSONMatcher` must satisfy the `GomegaMatcher` interface. It does this by implementing the `Match`, `FailureMessage`, and `NegatedFailureMessage` method: 1790 - If the `GomegaMatcher` receives invalid inputs `Match` returns a non-nil error explaining the problems with the input. This allows Gomega to fail the assertion whether the assertion is for the positive or negative case. 1791 - If the `actual` and `expected` values match, `Match` should return `true`. 1792 - Similarly, if the `actual` and `expected` values do not match, `Match` should return `false`. 1793 - If the `GomegaMatcher` was testing the `Should` case, and `Match` returned `false`, `FailureMessage` will be called to print a message explaining the failure. 1794 - Likewise, if the `GomegaMatcher` was testing the `ShouldNot` case, and `Match` returned `true`, `NegatedFailureMessage` will be called. 1795 - It is guaranteed that `FailureMessage` and `NegatedFailureMessage` will only be called *after* `Match`, so you can save off any state you need to compute the messages in `Match`. 1796 - Finally, it is common for matchers to make extensive use of the `reflect` library to interpret the generic inputs they receive. In this case, the `representJSONMatcher` goes through some `reflect` gymnastics to create a pointer to a new object with the same type as the `expected` object, read and decode JSON from `actual` into that pointer, and then deference the pointer and compare the result to the `expected` object. 1797 1798 ### gcustom: A convenient mechanism for building custom matchers 1799 1800 [`gcustom`](https://github.com/onsi/gomega/tree/master/gcustom) is a package that makes building custom matchers easy. Rather than define new types, you can simply provide `gcustom.MakeMatcher` with a function. The [godocs](https://pkg.go.dev/github.com/onsi/gomega/gcustom) for `gcustom` have all the details but here's how `RepresentJSONifiedObject` could be implemented with `gcustom`: 1801 1802 1803 ```go 1804 package json_response_matcher 1805 1806 import ( 1807 "github.com/onsi/gomega/types" 1808 "github.com/onsi/gomega/gcustom" 1809 1810 "encoding/json" 1811 "fmt" 1812 "net/http" 1813 "reflect" 1814 ) 1815 1816 func RepresentJSONifiedObject(expected interface{}) types.GomegaMatcher { 1817 return gcustom.MakeMatcher(func(response *http.Response) (bool, err) { 1818 pointerToObjectOfExpectedType := reflect.New(reflect.TypeOf(matcher.expected)).Interface() 1819 err = json.NewDecoder(response.Body).Decode(pointerToObjectOfExpectedType) 1820 if err != nil { 1821 return false, fmt.Errorf("Failed to decode JSON: %w", err.Error()) 1822 } 1823 1824 decodedObject := reflect.ValueOf(pointerToObjectOfExpectedType).Elem().Interface() 1825 return reflect.DeepEqual(decodedObject, matcher.expected), nil 1826 }).WithTemplate("Expected:\n{{.FormattedActual}}\n{{.To}} contain the JSON representation of\n{{format .Data 1}}").WithTemplateData(expected) 1827 } 1828 ``` 1829 1830 The [`gcustom` godocs](https://pkg.go.dev/github.com/onsi/gomega/gcustom) go into much more detail but we can point out a few of the convenient features of `gcustom` here: 1831 1832 - `gcustom` can take a matcher function that accepts a concrete type. In our case `func(response *https.Response) (bool, err)` - when this is done, the matcher built by `gcustom` takes care of all the type-checking for you and will only call your match function if an object of the correct type is asserted against. If you want to do your own type-checking (or want to build a matcher that works with multiple types) you can use `func(actual any) (bool, err)` instead. 1833 - Rather than implement different functions for the two different failure messages you can provide a single template. `gcustom` provides template variables to help you render the failure messages depending on positive failures vs negative failures. For example, the variable `{{.To}}` will render "to" for positive failures and "not to" for negative failures. 1834 - You can pass additional data to your template with `WithTemplateData(<data>)` - in this case we pass in the expected object so that the template can include it in the output. We do this with the expression `{{format .Data 1}}`. gcustom provides the `format` template function to render objects using Ginkgo's object formatting system (the `1` here denotes the level of indentation). 1835 1836 `gcustom` also supports a simpler mechanism for generating messages: `.WithMessage()` simply takes a string and builds a canned message out of that string. You can also provide precompiled templates if you want to avoid the cost of compiling a template every time the matcher is called. 1837 1838 ### Testing Custom Matchers 1839 1840 Whether you create a new `representJSONMatcher` type, or use `gcustom` you might test drive this matcher while writing it using Ginkgo. Your test might look like: 1841 1842 ```go 1843 package json_response_matcher_test 1844 1845 import ( 1846 . "github.com/onsi/ginkgo" 1847 . "github.com/onsi/gomega" 1848 . "jsonresponsematcher" 1849 1850 "bytes" 1851 "encoding/json" 1852 "io/ioutil" 1853 "net/http" 1854 "strings" 1855 1856 "testing" 1857 ) 1858 1859 func TestCustomMatcher(t *testing.T) { 1860 RegisterFailHandler(Fail) 1861 RunSpecs(t, "Custom Matcher Suite") 1862 } 1863 1864 type Book struct { 1865 Title string `json:"title"` 1866 Author string `json:"author"` 1867 } 1868 1869 var _ = Describe("RepresentJSONified Object", func() { 1870 var ( 1871 book Book 1872 bookJSON []byte 1873 response *http.Response 1874 ) 1875 1876 BeforeEach(func() { 1877 book = Book{ 1878 Title: "Les Miserables", 1879 Author: "Victor Hugo", 1880 } 1881 1882 var err error 1883 bookJSON, err = json.Marshal(book) 1884 Ω(err).ShouldNot(HaveOccurred()) 1885 }) 1886 1887 Context("when actual is not an http response", func() { 1888 It("should error", func() { 1889 _, err := RepresentJSONifiedObject(book).Match("not a response") 1890 Ω(err).Should(HaveOccurred()) 1891 }) 1892 }) 1893 1894 Context("when actual is an http response", func() { 1895 BeforeEach(func() { 1896 response = &http.Response{} 1897 }) 1898 1899 Context("with a body containing the JSON representation of actual", func() { 1900 BeforeEach(func() { 1901 response.ContentLength = int64(len(bookJSON)) 1902 response.Body = ioutil.NopCloser(bytes.NewBuffer(bookJSON)) 1903 }) 1904 1905 It("should succeed", func() { 1906 Ω(response).Should(RepresentJSONifiedObject(book)) 1907 }) 1908 }) 1909 1910 Context("with a body containing the JSON representation of something else", func() { 1911 BeforeEach(func() { 1912 reader := strings.NewReader(`{}`) 1913 response.ContentLength = int64(reader.Len()) 1914 response.Body = ioutil.NopCloser(reader) 1915 }) 1916 1917 It("should fail", func() { 1918 Ω(response).ShouldNot(RepresentJSONifiedObject(book)) 1919 }) 1920 }) 1921 1922 Context("with a body containing invalid JSON", func() { 1923 BeforeEach(func() { 1924 reader := strings.NewReader(`floop`) 1925 response.ContentLength = int64(reader.Len()) 1926 response.Body = ioutil.NopCloser(reader) 1927 }) 1928 1929 It("should error", func() { 1930 _, err := RepresentJSONifiedObject(book).Match(response) 1931 Ω(err).Should(HaveOccurred()) 1932 }) 1933 }) 1934 }) 1935 }) 1936 ``` 1937 1938 This also offers an example of what using the matcher would look like in your tests. Note that testing the cases when the matcher returns an error involves creating the matcher and invoking `Match` manually (instead of using an `Ω` or `Expect` assertion). 1939 1940 ### Aborting Eventually/Consistently 1941 1942 **Note: This section documents the `MatchMayChangeInTheFuture` method for aborting `Eventually`/`Consistently`. A more up-to-date approach that uses the `StopTrying` error is documented [earlier](#bailing-out-early--matchers).** 1943 1944 There are sometimes instances where `Eventually` or `Consistently` should stop polling a matcher because the result of the match simply cannot change. 1945 1946 For example, consider a test that looks like: 1947 1948 ```go 1949 Eventually(myChannel).Should(Receive(Equal("bar"))) 1950 ``` 1951 1952 `Eventually` will repeatedly invoke the `Receive` matcher against `myChannel` until the match succeeds. However, if the channel becomes *closed* there is *no way* for the match to ever succeed. Allowing `Eventually` to continue polling is inefficient and slows the test suite down. 1953 1954 To get around this, a matcher can optionally implement: 1955 1956 ```go 1957 MatchMayChangeInTheFuture(actual interface{}) bool 1958 ``` 1959 1960 This is not part of the `GomegaMatcher` interface and, in general, most matchers do not need to implement `MatchMayChangeInTheFuture`. 1961 1962 If implemented, however, `MatchMayChangeInTheFuture` will be called with the appropriate `actual` value by `Eventually` and `Consistently` *after* the call to `Match` during every polling interval. If `MatchMayChangeInTheFuture` returns `true`, `Eventually` and `Consistently` will continue polling. If, however, `MatchMayChangeInTheFuture` returns `false`, `Eventually` and `Consistently` will stop polling and either fail or pass as appropriate. 1963 1964 If you'd like to look at a simple example of `MatchMayChangeInTheFuture` check out [`gexec`'s `Exit` matcher](https://github.com/onsi/gomega/tree/master/gexec/exit_matcher.go). Here, `MatchMayChangeInTheFuture` returns true if the `gexec.Session` under test has not exited yet, but returns false if it has. Because of this, if a process exits with status code 3, but an assertion is made of the form: 1965 1966 ```go 1967 Eventually(session, 30).Should(gexec.Exit(0)) 1968 ``` 1969 1970 `Eventually` will not block for 30 seconds but will return (and fail, correctly) as soon as the mismatched exit code arrives! 1971 1972 > Note: `Eventually` and `Consistently` only exercise the `MatchMayChangeInTheFuture` method *if* they are passed a bare value. If they are passed functions to be polled it is not possible to guarantee that the return value of the function will not change between polling intervals. In this case, `MatchMayChangeInTheFuture` is not called and the polling continues until either a match is found or the timeout elapses. 1973 1974 ### Contributing to Gomega 1975 1976 Contributions are more than welcome. Either [open an issue](http://github.com/onsi/gomega/issues) for a matcher you'd like to see or, better yet, test drive the matcher and [send a pull request](https://github.com/onsi/gomega/pulls). 1977 1978 When adding a new matcher please mimic the style use in Gomega's current matchers: you should use the `format` package to format your output, put the matcher and its tests in the `matchers` package, and the constructor in the `matchers.go` file in the top-level package. 1979 1980 ## Extending Gomega 1981 1982 The default Gomega can be wrapped by replacing it with an object that implements both the `gomega.Gomega` interface and the `inner` interface: 1983 1984 ```go 1985 type inner interface { 1986 Inner() Gomega 1987 } 1988 ``` 1989 1990 The `Inner()` method must return the actual `gomega.Default`. For Gomega to function properly your wrapper methods must call the same method on the real `gomega.Default` This allows you to wrap every Gomega method call (e.g. `Expect()`) with your own code across your test suite. You can use this to add random delays, additional logging, or just for tracking the number of `Expect()` calls made. 1991 1992 ```go 1993 func init() { 1994 gomega.Default = &gomegaWrapper{ 1995 inner: gomega.Default, 1996 } 1997 } 1998 1999 type gomegaWrapper struct { 2000 inner gomega.Gomega 2001 } 2002 func (g *gomegaWrapper) Inner() gomega.Gomega { 2003 return g.inner 2004 } 2005 func (g *gomegaWrapper) Ω(actual interface{}, extra ...interface{}) types.Assertion { 2006 // You now have an opportunity to add a random delay to help identify any timing 2007 // dependencies in your tests or can add additional logging. 2008 return g.inner.Ω(actual, extra...) 2009 } 2010 ... 2011 ``` 2012 2013 ## `ghttp`: Testing HTTP Clients 2014 The `ghttp` package provides support for testing http *clients*. The typical pattern in Go for testing http clients entails spinning up an `httptest.Server` using the `net/http/httptest` package and attaching test-specific handlers that perform assertions. 2015 2016 `ghttp` provides `ghttp.Server` - a wrapper around `httptest.Server` that allows you to easily build up a stack of test handlers. These handlers make assertions against the incoming request and return a pre-fabricated response. `ghttp` provides a number of prebuilt handlers that cover the most common assertions. You can combine these handlers to build out full-fledged assertions that test multiple aspects of the incoming requests. 2017 2018 The goal of this documentation is to provide you with an adequate mental model to use `ghttp` correctly. For a full reference of all the available handlers and the various methods on `ghttp.Server` look at the [godoc](https://godoc.org/github.com/onsi/gomega/ghttp) documentation. 2019 2020 ### Making assertions against an incoming request 2021 2022 Let's start with a simple example. Say you are building an API client that provides a `FetchSprockets(category string)` method that makes an http request to a remote server to fetch sprockets of a given category. 2023 2024 For now, let's not worry about the values returned by `FetchSprockets` but simply assert that the correct request was made. Here's the setup for our `ghttp`-based Ginkgo test: 2025 2026 ```go 2027 Describe("The sprockets client", func() { 2028 var server *ghttp.Server 2029 var client *sprockets.Client 2030 2031 BeforeEach(func() { 2032 server = ghttp.NewServer() 2033 client = sprockets.NewClient(server.URL()) 2034 }) 2035 2036 AfterEach(func() { 2037 //shut down the server between tests 2038 server.Close() 2039 }) 2040 }) 2041 ``` 2042 2043 Note that the server's URL is auto-generated and varies between test runs. Because of this, you must always inject the server URL into your client. Let's add a simple test that asserts that `FetchSprockets` hits the correct endpoint with the correct HTTP verb: 2044 2045 ```go 2046 Describe("The sprockets client", func() { 2047 //...see above 2048 2049 Describe("fetching sprockets", func() { 2050 BeforeEach(func() { 2051 server.AppendHandlers( 2052 ghttp.VerifyRequest("GET", "/sprockets"), 2053 ) 2054 }) 2055 2056 It("should make a request to fetch sprockets", func() { 2057 client.FetchSprockets("") 2058 Ω(server.ReceivedRequests()).Should(HaveLen(1)) 2059 }) 2060 }) 2061 }) 2062 ``` 2063 2064 Here we append a `VerifyRequest` handler to the `server` and call `client.FetchSprockets`. This call (assuming it's a blocking call) will make a round-trip to the test `server` before returning. The test `server` receives the request and passes it through the `VerifyRequest` handler which will validate that the request is a `GET` request hitting the `/sprockets` endpoint. If it's not, the test will fail. 2065 2066 Note that the test can pass trivially if `client.FetchSprockets()` doesn't actually make a request. To guard against this you can assert that the `server` has actually received a request. All the requests received by the server are saved off and made available via `server.ReceivedRequests()`. We use this to assert that there should have been exactly one received requests. 2067 2068 > Guarding against the trivial "false positive" case outlined above isn't really necessary. Just good practice when test *driving*. 2069 2070 Let's add some more to our example. Let's make an assertion that `FetchSprockets` can request sprockets filtered by a particular category: 2071 2072 ```go 2073 Describe("The sprockets client", func() { 2074 //...see above 2075 2076 Describe("fetching sprockets", func() { 2077 BeforeEach(func() { 2078 server.AppendHandlers( 2079 ghttp.VerifyRequest("GET", "/sprockets", "category=encabulators"), 2080 ) 2081 }) 2082 2083 It("should make a request to fetch sprockets", func() { 2084 client.FetchSprockets("encabulators") 2085 Ω(server.ReceivedRequests()).Should(HaveLen(1)) 2086 }) 2087 }) 2088 }) 2089 ``` 2090 2091 `ghttp.VerifyRequest` takes an optional third parameter that is matched against the request `URL`'s `RawQuery`. 2092 2093 Let's extend the example some more. In addition to asserting that the request is a `GET` request to the correct endpoint with the correct query params, let's also assert that it includes the correct `BasicAuth` information and a correct custom header. Here's the complete example: 2094 2095 ```go 2096 Describe("The sprockets client", func() { 2097 var ( 2098 server *ghttp.Server 2099 client *sprockets.Client 2100 username, password string 2101 ) 2102 2103 BeforeEach(func() { 2104 username, password = "gopher", "tacoshell" 2105 server = ghttp.NewServer() 2106 client = sprockets.NewClient(server.URL(), username, password) 2107 }) 2108 2109 AfterEach(func() { 2110 server.Close() 2111 }) 2112 2113 Describe("fetching sprockets", func() { 2114 BeforeEach(func() { 2115 server.AppendHandlers( 2116 ghttp.CombineHandlers( 2117 ghttp.VerifyRequest("GET", "/sprockets", "category=encabulators"), 2118 ghttp.VerifyBasicAuth(username, password), 2119 ghttp.VerifyHeader(http.Header{ 2120 "X-Sprocket-API-Version": []string{"1.0"}, 2121 }), 2122 ) 2123 ) 2124 }) 2125 2126 It("should make a request to fetch sprockets", func() { 2127 client.FetchSprockets("encabulators") 2128 Ω(server.ReceivedRequests()).Should(HaveLen(1)) 2129 }) 2130 }) 2131 }) 2132 ``` 2133 2134 This example *combines* multiple `ghttp` verify handlers using `ghttp.CombineHandlers`. Under the hood, this returns a new handler that wraps and invokes the three passed in verify handlers. The request sent by the client will pass through each of these verify handlers and must pass them all for the test to pass. 2135 2136 Note that you can easily add your own verify handler into the mix. Just pass in a regular `http.HandlerFunc` and make assertions against the received request. 2137 2138 > It's important to understand that you must pass `AppendHandlers` **one** handler *per* incoming request (see [below](#handling-multiple-requests)). In order to apply multiple handlers to a single request we must first combine them with `ghttp.CombineHandlers` and then pass that *one* wrapper handler in to `AppendHandlers`. 2139 2140 ### Providing responses 2141 2142 So far, we've only made assertions about the outgoing request. Clients are also responsible for parsing responses and returning valid data. Let's say that `FetchSprockets()` returns two things: a slice `[]Sprocket` and an `error`. Here's what a happy path test that asserts the correct data is returned might look like: 2143 2144 ```go 2145 Describe("The sprockets client", func() { 2146 //... 2147 Describe("fetching sprockets", func() { 2148 BeforeEach(func() { 2149 server.AppendHandlers( 2150 ghttp.CombineHandlers( 2151 ghttp.VerifyRequest("GET", "/sprockets", "category=encabulators"), 2152 ghttp.VerifyBasicAuth(username, password), 2153 ghttp.VerifyHeader(http.Header{ 2154 "X-Sprocket-API-Version": []string{"1.0"}, 2155 }), 2156 ghttp.RespondWith(http.StatusOK, `[ 2157 {"name": "entropic decoupler", "color": "red"}, 2158 {"name": "defragmenting ramjet", "color": "yellow"} 2159 ]`), 2160 ) 2161 ) 2162 }) 2163 2164 It("should make a request to fetch sprockets", func() { 2165 sprockets, err := client.FetchSprockets("encabulators") 2166 Ω(err).ShouldNot(HaveOccurred()) 2167 Ω(sprockets).Should(Equal([]Sprocket{ 2168 sprockets.Sprocket{Name: "entropic decoupler", Color: "red"}, 2169 sprockets.Sprocket{Name: "defragmenting ramjet", Color: "yellow"}, 2170 })) 2171 }) 2172 }) 2173 }) 2174 ``` 2175 2176 We use `ghttp.RespondWith` to specify the response return by the server. In this case we're passing back a status code of `200` (`http.StatusOK`) and a pile of JSON. We then assert, in the test, that the client succeeds and returns the correct set of sprockets. 2177 2178 The fact that details of the JSON encoding are bleeding into this test is somewhat unfortunate, and there's a lot of repetition going on. `ghttp` provides a `RespondWithJSONEncoded` handler that accepts an arbitrary object and JSON encodes it for you. Here's a cleaner test: 2179 2180 ```go 2181 Describe("The sprockets client", func() { 2182 //... 2183 Describe("fetching sprockets", func() { 2184 var returnedSprockets []Sprocket 2185 BeforeEach(func() { 2186 returnedSprockets = []Sprocket{ 2187 sprockets.Sprocket{Name: "entropic decoupler", Color: "red"}, 2188 sprockets.Sprocket{Name: "defragmenting ramjet", Color: "yellow"}, 2189 } 2190 2191 server.AppendHandlers( 2192 ghttp.CombineHandlers( 2193 ghttp.VerifyRequest("GET", "/sprockets", "category=encabulators"), 2194 ghttp.VerifyBasicAuth(username, password), 2195 ghttp.VerifyHeader(http.Header{ 2196 "X-Sprocket-API-Version": []string{"1.0"}, 2197 }), 2198 ghttp.RespondWithJSONEncoded(http.StatusOK, returnedSprockets), 2199 ) 2200 ) 2201 }) 2202 2203 It("should make a request to fetch sprockets", func() { 2204 sprockets, err := client.FetchSprockets("encabulators") 2205 Ω(err).ShouldNot(HaveOccurred()) 2206 Ω(sprockets).Should(Equal(returnedSprockets)) 2207 }) 2208 }) 2209 }) 2210 ``` 2211 2212 ### Testing different response scenarios 2213 2214 Our test currently only handles the happy path where the server returns a `200`. We should also test a handful of sad paths. In particular, we'd like to return a `SprocketsErrorNotFound` error when the server `404`s and a `SprocketsErrorUnauthorized` error when the server returns a `401`. But how to do this without redefining our server handler three times? 2215 2216 `ghttp` provides `RespondWithPtr` and `RespondWithJSONEncodedPtr` for just this use case. Both take *pointers* to status codes and respond bodies (objects for the `JSON` case). Here's the more complete test: 2217 2218 ```go 2219 Describe("The sprockets client", func() { 2220 //... 2221 Describe("fetching sprockets", func() { 2222 var returnedSprockets []Sprocket 2223 var statusCode int 2224 2225 BeforeEach(func() { 2226 returnedSprockets = []Sprocket{ 2227 sprockets.Sprocket{Name: "entropic decoupler", Color: "red"}, 2228 sprockets.Sprocket{Name: "defragmenting ramjet", Color: "yellow"}, 2229 } 2230 2231 server.AppendHandlers( 2232 ghttp.CombineHandlers( 2233 ghttp.VerifyRequest("GET", "/sprockets", "category=encabulators"), 2234 ghttp.VerifyBasicAuth(username, password), 2235 ghttp.VerifyHeader(http.Header{ 2236 "X-Sprocket-API-Version": []string{"1.0"}, 2237 }), 2238 ghttp.RespondWithJSONEncodedPtr(&statusCode, &returnedSprockets), 2239 ) 2240 ) 2241 }) 2242 2243 Context("when the request succeeds", func() { 2244 BeforeEach(func() { 2245 statusCode = http.StatusOK 2246 }) 2247 2248 It("should return the fetched sprockets without erroring", func() { 2249 sprockets, err := client.FetchSprockets("encabulators") 2250 Ω(err).ShouldNot(HaveOccurred()) 2251 Ω(sprockets).Should(Equal(returnedSprockets)) 2252 }) 2253 }) 2254 2255 Context("when the response is unauthorized", func() { 2256 BeforeEach(func() { 2257 statusCode = http.StatusUnauthorized 2258 }) 2259 2260 It("should return the SprocketsErrorUnauthorized error", func() { 2261 sprockets, err := client.FetchSprockets("encabulators") 2262 Ω(sprockets).Should(BeEmpty()) 2263 Ω(err).Should(MatchError(SprocketsErrorUnauthorized)) 2264 }) 2265 }) 2266 2267 Context("when the response is not found", func() { 2268 BeforeEach(func() { 2269 statusCode = http.StatusNotFound 2270 }) 2271 2272 It("should return the SprocketsErrorNotFound error", func() { 2273 sprockets, err := client.FetchSprockets("encabulators") 2274 Ω(sprockets).Should(BeEmpty()) 2275 Ω(err).Should(MatchError(SprocketsErrorNotFound)) 2276 }) 2277 }) 2278 }) 2279 }) 2280 ``` 2281 2282 In this way, the status code and returned value (not shown here) can be changed in sub-contexts without having to modify the original test setup. 2283 2284 ### Handling multiple requests 2285 2286 So far, we've only seen examples where one request is made per test. `ghttp` supports handling *multiple* requests too. `server.AppendHandlers` can be passed multiple handlers and these handlers are evaluated in order as requests come in. 2287 2288 This can be helpful in cases where it is not possible (or desirable) to have calls to the client under test only generate *one* request. A common example is pagination. If the sprockets API is paginated it may be desirable for `FetchSprockets` to provide a simpler interface that simply fetches all available sprockets. 2289 2290 Here's what a test might look like: 2291 2292 ```go 2293 Describe("fetching sprockets from a paginated endpoint", func() { 2294 var returnedSprockets []Sprocket 2295 var firstResponse, secondResponse PaginatedResponse 2296 2297 BeforeEach(func() { 2298 returnedSprockets = []Sprocket{ 2299 sprockets.Sprocket{Name: "entropic decoupler", Color: "red"}, 2300 sprockets.Sprocket{Name: "defragmenting ramjet", Color: "yellow"}, 2301 sprockets.Sprocket{Name: "parametric demuxer", Color: "blue"}, 2302 } 2303 2304 firstResponse = sprockets.PaginatedResponse{ 2305 Sprockets: returnedSprockets[0:2], //first batch 2306 PaginationToken: "get-second-batch", //some opaque non-empty token 2307 } 2308 2309 secondResponse = sprockets.PaginatedResponse{ 2310 Sprockets: returnedSprockets[2:], //second batch 2311 PaginationToken: "", //signifies the last batch 2312 } 2313 2314 server.AppendHandlers( 2315 ghttp.CombineHandlers( 2316 ghttp.VerifyRequest("GET", "/sprockets", "category=encabulators"), 2317 ghttp.RespondWithJSONEncoded(http.StatusOK, firstResponse), 2318 ), 2319 ghttp.CombineHandlers( 2320 ghttp.VerifyRequest("GET", "/sprockets", "category=encabulators&pagination-token=get-second-batch"), 2321 ghttp.RespondWithJSONEncoded(http.StatusOK, secondResponse), 2322 ), 2323 ) 2324 }) 2325 2326 It("should fetch all the sprockets", func() { 2327 sprockets, err := client.FetchSprockets("encabulators") 2328 Ω(err).ShouldNot(HaveOccurred()) 2329 Ω(sprockets).Should(Equal(returnedSprockets)) 2330 }) 2331 }) 2332 ``` 2333 2334 By default the `ghttp` server fails the test if the number of requests received exceeds the number of handlers registered, so this test ensures that the `client` stops sending requests after receiving the second (and final) set of paginated data. 2335 2336 ### MUXing Routes to Handlers 2337 2338 `AppendHandlers` allows you to make ordered assertions about incoming requests. This places a strong constraint on all incoming requests: namely that exactly the right requests have to arrive in exactly the right order and that no additional requests are allowed. 2339 2340 One can take a different testing strategy, however. Instead of asserting that requests come in in a predefined order, you may which to build a test server that can handle arbitrarily many requests to a set of predefined routes. In fact, there may be some circumstances where you want to make ordered assertions on *some* requests (via `AppendHandlers`) but still support sending particular responses to *other* requests that may interleave the ordered assertions. 2341 2342 `ghttp` supports these sorts of usecases via `server.RouteToHandler(method, path, handler)`. 2343 2344 Let's cook up an example. Perhaps, instead of authenticating via basic auth our sprockets client logs in and fetches a token from the server when performing requests that require authentication. We could pepper our `AppendHandlers` calls with a handler that handles these requests (this is not a terrible idea, of course!) *or* we could set up a single route at the top of our tests. 2345 2346 Here's what such a test might look like: 2347 2348 ```go 2349 Describe("CRUDing sprockes", func() { 2350 BeforeEach(func() { 2351 server.RouteToHandler("POST", "/login", ghttp.CombineHandlers( 2352 ghttp.VerifyRequest("POST", "/login", "user=bob&password=password"), 2353 ghttp.RespondWith(http.StatusOK, "your-auth-token"), 2354 )) 2355 }) 2356 Context("GETting sprockets", func() { 2357 var returnedSprockets []Sprocket 2358 2359 BeforeEach(func() { 2360 returnedSprockets = []Sprocket{ 2361 sprockets.Sprocket{Name: "entropic decoupler", Color: "red"}, 2362 sprockets.Sprocket{Name: "defragmenting ramjet", Color: "yellow"}, 2363 sprockets.Sprocket{Name: "parametric demuxer", Color: "blue"}, 2364 } 2365 2366 server.AppendHandlers( 2367 ghttp.CombineHandlers( 2368 ghttp.VerifyRequest("GET", "/sprockets", "category=encabulators"), 2369 ghttp.RespondWithJSONEncoded(http.StatusOK, returnedSprockets), 2370 ), 2371 ) 2372 }) 2373 2374 It("should fetch all the sprockets", func() { 2375 sprockets, err := client.FetchSprockes("encabulators") 2376 Ω(err).ShouldNot(HaveOccurred()) 2377 Ω(sprockets).Should(Equal(returnedSprockets)) 2378 }) 2379 }) 2380 2381 Context("POSTing sprockets", func() { 2382 var sprocketToSave Sprocket 2383 BeforeEach(func() { 2384 sprocketToSave = sprockets.Sprocket{Name: "endothermic penambulator", Color: "purple"} 2385 2386 server.AppendHandlers( 2387 ghttp.CombineHandlers( 2388 ghttp.VerifyRequest("POST", "/sprocket", "token=your-auth-token"), 2389 ghttp.VerifyJSONRepresenting(sprocketToSave) 2390 ghttp.RespondWithJSONEncoded(http.StatusOK, nil), 2391 ), 2392 ) 2393 }) 2394 2395 It("should save the sprocket", func() { 2396 err := client.SaveSprocket(sprocketToSave) 2397 Ω(err).ShouldNot(HaveOccurred()) 2398 }) 2399 }) 2400 }) 2401 ``` 2402 2403 Here, saving a sprocket triggers authentication, which is handled by the registered `RouteToHandler` handler whereas fetching the list of sprockets does not. 2404 2405 > `RouteToHandler` can take either a string as a route (as seen in this example) or a `regexp.Regexp`. 2406 2407 ### Allowing unhandled requests 2408 2409 By default, `ghttp`'s server marks the test as failed if a request is made for which there is no registered handler. 2410 2411 It is sometimes useful to have a fake server that simply returns a fixed status code for all unhandled incoming requests. `ghttp` supports this: just call `server.SetAllowUnhandledRequests(true)` and `server.SetUnhandledRequestStatusCode(statusCode)`, passing whatever status code you'd like to return. 2412 2413 In addition to returning the registered status code, `ghttp`'s server will also save all received requests. These can be accessed by calling `server.ReceivedRequests()`. This is useful for cases where you may want to make assertions against requests *after* they've been made. 2414 2415 To bring it all together: there are three ways to instruct a `ghttp` server to handle requests: you can map routes to handlers using `RouteToHandler`, you can append handlers via `AppendHandlers`, and you can `SetAllowUnhandledRequests` and specify the status code by calling `SetUnhandledRequestStatusCode`. 2416 2417 When a `ghttp` server receives a request it first checks against the set of handlers registered via `RouteToHandler` if there is no such handler it proceeds to pop an `AppendHandlers` handler off the stack, if the stack of ordered handlers is empty, it will check whether `GetAllowUnhandledRequests` returns `true` or `false`. If `false` the test fails. If `true`, a response is sent with whatever `GetUnhandledRequestStatusCode` returns. 2418 2419 ## `gbytes`: Testing Streaming Buffers 2420 2421 `gbytes` implements `gbytes.Buffer` - an `io.WriteCloser` that captures all input to an in-memory buffer. 2422 2423 When used in concert with the `gbytes.Say` matcher, the `gbytes.Buffer` allows you make *ordered* assertions against streaming data. 2424 2425 What follows is a contrived example. `gbytes` is best paired with [`gexec`](#gexec-testing-external-processes). 2426 2427 Say you have an integration test that is streaming output from an external API. You can feed this stream into a `gbytes.Buffer` and make ordered assertions like so: 2428 2429 ```go 2430 Describe("attach to the data stream", func() { 2431 var ( 2432 client *apiclient.Client 2433 buffer *gbytes.Buffer 2434 ) 2435 BeforeEach(func() { 2436 buffer = gbytes.NewBuffer() 2437 client := apiclient.New() 2438 go client.AttachToDataStream(buffer) 2439 }) 2440 2441 It("should stream data", func() { 2442 Eventually(buffer).Should(gbytes.Say(`Attached to stream as client \d+`)) 2443 2444 client.ReticulateSplines() 2445 Eventually(buffer).Should(gbytes.Say(`reticulating splines`)) 2446 client.EncabulateRetros(7) 2447 Eventually(buffer).Should(gbytes.Say(`encabulating 7 retros`)) 2448 }) 2449 }) 2450 ``` 2451 2452 These assertions will only pass if the strings passed to `Say` (which are interpreted as regular expressions - make sure to escape characters appropriately!) appear in the buffer. An opaque read cursor (that you cannot access or modify) is fast-forwarded as successful assertions are made. So, for example: 2453 2454 ```go 2455 Eventually(buffer).Should(gbytes.Say(`reticulating splines`)) 2456 Consistently(buffer).ShouldNot(gbytes.Say(`reticulating splines`)) 2457 ``` 2458 2459 will (counterintuitively) pass. This allows you to write tests like: 2460 2461 ```go 2462 client.ReticulateSplines() 2463 Eventually(buffer).Should(gbytes.Say(`reticulating splines`)) 2464 client.ReticulateSplines() 2465 Eventually(buffer).Should(gbytes.Say(`reticulating splines`)) 2466 ``` 2467 2468 and ensure that the test is correctly asserting that `reticulating splines` appears *twice*. 2469 2470 At any time, you can access the entire contents written to the buffer via `buffer.Contents()`. This includes *everything* ever written to the buffer regardless of the current position of the read cursor. 2471 2472 ### Handling branches 2473 2474 Sometimes (rarely!) you must write a test that must perform different actions depending on the output streamed to the buffer. This can be accomplished using `buffer.Detect`. Here's a contrived example: 2475 2476 ```go 2477 func LoginIfNecessary() { 2478 client.Authorize() 2479 select { 2480 case <-buffer.Detect("You are not logged in"): 2481 client.Login() 2482 case <-buffer.Detect("Success"): 2483 return 2484 case <-time.After(time.Second): 2485 ginkgo.Fail("timed out waiting for output") 2486 } 2487 buffer.CancelDetects() 2488 } 2489 ``` 2490 2491 `buffer.Detect` takes a string (interpreted as a regular expression) and returns a channel that will fire *once* if the requested string is detected. Upon detection, the buffer's opaque read cursor is fast-forwarded so subsequent uses of `gbytes.Say` will pick up from where the succeeding `Detect` left off. You *must* call `buffer.CancelDetects()` to clean up afterwards (`buffer` spawns one goroutine per call to `Detect`). 2492 2493 ### Testing `io.Reader`s, `io.Writer`s, and `io.Closer`s 2494 2495 Implementations of `io.Reader`, `io.Writer`, and `io.Closer` are expected to be blocking. This makes the following class of tests unsafe: 2496 2497 ```go 2498 It("should read something", func() { 2499 p := make([]byte, 5) 2500 _, err := reader.Read(p) //unsafe! this could block forever 2501 Ω(err).ShouldNot(HaveOccurred()) 2502 Ω(p).Should(Equal([]byte("abcde"))) 2503 }) 2504 ``` 2505 2506 It is safer to wrap `io.Reader`s, `io.Writer`s, and `io.Closer`s with explicit timeouts. You can do this with `gbytes.TimeoutReader`, `gbytes.TimeoutWriter`, and `gbytes.TimeoutCloser` like so: 2507 2508 ```go 2509 It("should read something", func() { 2510 p := make([]byte, 5) 2511 _, err := gbytes.TimeoutReader(reader, time.Second).Read(p) 2512 Ω(err).ShouldNot(HaveOccurred()) 2513 Ω(p).Should(Equal([]byte("abcde"))) 2514 }) 2515 ``` 2516 2517 The `gbytes` wrappers will return `gbytes.ErrTimeout` if a timeout occurs. 2518 2519 In the case of `io.Reader`s you can leverage the `Say` matcher and the functionality of `gbytes.Buffer` by building a `gbytes.Buffer` that reads from the `io.Reader` asynchronously. You can do this with the `gbytes` package like so: 2520 2521 ```go 2522 It("should read something", func() { 2523 Eventually(gbytes.BufferReader(reader)).Should(gbytes.Say("abcde")) 2524 }) 2525 ``` 2526 2527 `gbytes.BufferReader` takes an `io.Reader` and returns a `gbytes.Buffer`. Under the hood an `io.Copy` goroutine is launched to copy data from the `io.Reader` into the `gbytes.Buffer`. The `gbytes.Buffer` is closed when the `io.Copy` completes. Because the `io.Copy` is launched asynchronously you *must* make assertions against the reader using `Eventually`. 2528 2529 2530 ## `gexec`: Testing External Processes 2531 2532 `gexec` simplifies testing external processes. It can help you [compile go binaries](#compiling-external-binaries), [start external processes](#starting-external-processes), [send signals and wait for them to exit](#sending-signals-and-waiting-for-the-process-to-exit), make [assertions against the exit code](#asserting-against-exit-code), and stream output into `gbytes.Buffer`s to allow you [make assertions against output](#making-assertions-against-the-process-output). 2533 2534 ### Compiling external binaries 2535 2536 You use `gexec.Build()` to compile Go binaries. These are built using `go build` and are stored off in a temporary directory. You'll want to `gexec.CleanupBuildArtifacts()` when you're done with the test. 2537 2538 A common pattern is to compile binaries once at the beginning of the test using `BeforeSuite` and to clean up once at the end of the test using `AfterSuite`: 2539 2540 ```go 2541 var pathToSprocketCLI string 2542 2543 BeforeSuite(func() { 2544 var err error 2545 pathToSprocketCLI, err = gexec.Build("github.com/spacely/sprockets") 2546 Ω(err).ShouldNot(HaveOccurred()) 2547 }) 2548 2549 AfterSuite(func() { 2550 gexec.CleanupBuildArtifacts() 2551 }) 2552 ``` 2553 2554 > By default, `gexec.Build` uses the GOPATH specified in your environment. You can also use `gexec.BuildIn(gopath string, packagePath string)` to specify a custom GOPATH for the build command. This is useful to, for example, build a binary against its vendored Go dependencies. 2555 2556 > You can specify arbitrary environment variables for the build command – such as GOOS and GOARCH for building on other platforms – using `gexec.BuildWithEnvironment(packagePath string, envs []string)`. 2557 2558 ### Starting external processes 2559 2560 `gexec` provides a `Session` that wraps `exec.Cmd`. `Session` includes a number of features that will be explored in the next few sections. You create a `Session` by instructing `gexec` to start a command: 2561 2562 ```go 2563 command := exec.Command(pathToSprocketCLI, "-api=127.0.0.1:8899") 2564 session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) 2565 Ω(err).ShouldNot(HaveOccurred()) 2566 ``` 2567 2568 `gexec.Start` calls `command.Start` for you and forwards the command's `stdout` and `stderr` to `io.Writer`s that you provide. In the code above, we pass in Ginkgo's `GinkgoWriter`. This makes working with external processes quite convenient: when a test passes no output is printed to screen, however if a test fails then any output generated by the command will be provided. 2569 2570 > If you want to see all your output regardless of test status, just run `ginkgo` in verbose mode (`-v`) - now everything written to `GinkgoWriter` makes it onto the screen. 2571 2572 ### Sending signals and waiting for the process to exit 2573 2574 `gexec.Session` makes it easy to send signals to your started command: 2575 2576 ```go 2577 session.Kill() //sends SIGKILL 2578 session.Interrupt() //sends SIGINT 2579 session.Terminate() //sends SIGTERM 2580 session.Signal(signal) //sends the passed in os.Signal signal 2581 ``` 2582 2583 If the process has already exited these signal calls are no-ops. 2584 2585 In addition to starting the wrapped command, `gexec.Session` also *monitors* the command until it exits. You can ask `gexec.Session` to `Wait` until the process exits: 2586 2587 ```go 2588 session.Wait() 2589 ``` 2590 2591 this will block until the session exits and will *fail* if it does not exit within the default `Eventually` timeout. You can override this timeout by specifying a custom one: 2592 2593 ```go 2594 session.Wait(5 * time.Second) 2595 ``` 2596 2597 > Though you can access the wrapped command using `session.Command` you should not attempt to `Wait` on it yourself. `gexec` has already called `Wait` in order to monitor your process for you. 2598 2599 > Under the hood `session.Wait` simply uses `Eventually`. 2600 2601 2602 Since the signaling methods return the session you can chain calls together: 2603 2604 ```go 2605 session.Terminate().Wait() 2606 ``` 2607 2608 will send `SIGTERM` and then wait for the process to exit. 2609 2610 ### Asserting against exit code 2611 2612 Once a session has exited you can fetch its exit code with `session.ExitCode()`. You can subsequently make assertions against the exit code. 2613 2614 A more idiomatic way to assert that a command has exited is to use the `gexec.Exit()` matcher: 2615 2616 ```go 2617 Eventually(session).Should(Exit()) 2618 ``` 2619 2620 Will verify that the `session` exits within `Eventually`'s default timeout. You can assert that the process exits with a specified exit code too: 2621 2622 ```go 2623 Eventually(session).Should(Exit(0)) 2624 ``` 2625 2626 > If the process has not exited yet, `session.ExitCode()` returns `-1` 2627 2628 ### Making assertions against the process output 2629 2630 In addition to streaming output to the passed in `io.Writer`s (the `GinkgoWriter` in our example above), `gexec.Start` attaches `gbytes.Buffer`s to the command's output streams. These are available on the `session` object via: 2631 2632 ```go 2633 session.Out //a gbytes.Buffer connected to the command's stdout 2634 session.Err //a gbytes.Buffer connected to the command's stderr 2635 ``` 2636 2637 This allows you to make assertions against the stream of output: 2638 2639 ```go 2640 Eventually(session.Out).Should(gbytes.Say("hello [A-Za-z], nice to meet you")) 2641 Eventually(session.Err).Should(gbytes.Say("oops!")) 2642 ``` 2643 2644 Since `gexec.Session` is a `gbytes.BufferProvider` that provides the `Out` buffer you can write assertions against `stdout` output like so: 2645 2646 ```go 2647 Eventually(session).Should(gbytes.Say("hello [A-Za-z], nice to meet you")) 2648 ``` 2649 2650 Using the `Say` matcher is convenient when making *ordered* assertions against a stream of data generated by a live process. Sometimes, however, all you need is to 2651 wait for the process to exit and then make assertions against the entire contents of its output. Since `Wait()` returns `session` you can wait for the process to exit, then grab all its stdout as a `[]byte` buffer with a simple one-liner: 2652 2653 ```go 2654 Ω(session.Wait().Out.Contents()).Should(ContainSubstring("finished successfully")) 2655 ``` 2656 2657 ### Signaling all processes 2658 `gexec` provides methods to track and send signals to all processes that it starts. 2659 2660 ```go 2661 gexec.Kill() //sends SIGKILL to all processes 2662 gexec.Terminate() //sends SIGTERM to all processes 2663 gexec.Signal(int) //sends the passed in os.Signal signal to all the processes 2664 gexec.Interrupt() //sends SIGINT to all processes 2665 ``` 2666 2667 If the any of the processes have already exited these signal calls are no-ops. 2668 2669 `gexec` also provides methods to cleanup and wait for all the processes it started. 2670 2671 ```go 2672 gexec.KillAndWait() 2673 gexec.TerminateAndWait() 2674 ``` 2675 2676 You can specify a custom timeout by: 2677 2678 ```go 2679 gexec.KillAndWait(5 * time.Second) 2680 gexec.TerminateAndWait(2 * time.Second) 2681 ``` 2682 2683 The timeout is applied for each of the processes. 2684 2685 It is considered good practice to ensure all of your processes have been killed before the end of the test suite. If you are using `ginkgo` you can use: 2686 2687 ```go 2688 AfterSuite(func(){ 2689 gexec.KillAndWait() 2690 }) 2691 ``` 2692 2693 Due to the global nature of these methods, keep in mind that signaling processes will affect all processes started by `gexec`, in any context. For example if these methods where used in an `AfterEach`, then processes started in `BeforeSuite` would also be signaled. 2694 2695 ## `gstruct`: Testing Complex Data Types 2696 2697 `gstruct` simplifies testing large and nested structs and slices. It is used for building up complex matchers that apply different tests to each field or element. 2698 2699 ### Testing type `struct` 2700 2701 `gstruct` provides the `FieldsMatcher` through the `MatchAllFields` and `MatchFields` functions for applying a separate matcher to each field of a struct: 2702 2703 ```go 2704 actual := struct{ 2705 A int 2706 B bool 2707 C string 2708 }{5, true, "foo"} 2709 Expect(actual).To(MatchAllFields(Fields{ 2710 "A": BeNumerically("<", 10), 2711 "B": BeTrue(), 2712 "C": Equal("foo"), 2713 })) 2714 ``` 2715 2716 `MatchAllFields` requires that every field is matched, and each matcher is mapped to a field. To match a subset or superset of a struct, you should use the `MatchFields` function with the `IgnoreExtras` and `IgnoreMissing` options. `IgnoreExtras` will ignore fields that don't map to a matcher, e.g. 2717 2718 ```go 2719 Expect(actual).To(MatchFields(IgnoreExtras, Fields{ 2720 "A": BeNumerically("<", 10), 2721 "B": BeTrue(), 2722 // Ignore lack of "C" in the matcher. 2723 })) 2724 ``` 2725 2726 `IgnoreMissing` will ignore matchers that don't map to a field, e.g. 2727 2728 ```go 2729 Expect(actual).To(MatchFields(IgnoreMissing, Fields{ 2730 "A": BeNumerically("<", 10), 2731 "B": BeTrue(), 2732 "C": Equal("foo"), 2733 "D": Equal("bar"), // Ignored, since actual.D does not exist. 2734 })) 2735 ``` 2736 2737 The options can be combined with the binary or: `IgnoreMissing|IgnoreExtras`. 2738 2739 ### Testing type slice 2740 2741 `gstruct` provides the `ElementsMatcher` through the `MatchAllElements` and `MatchElements` function for applying a separate matcher to each element, identified by an `Identifier` function: 2742 2743 ```go 2744 actual := []string{ 2745 "A: foo bar baz", 2746 "B: once upon a time", 2747 "C: the end", 2748 } 2749 id := func(element interface{}) string { 2750 return string(element.(string)[0]) 2751 } 2752 Expect(actual).To(MatchAllElements(id, Elements{ 2753 "A": Not(BeZero()), 2754 "B": MatchRegexp("[A-Z]: [a-z ]+"), 2755 "C": ContainSubstring("end"), 2756 })) 2757 ``` 2758 2759 `MatchAllElements` requires that there is a 1:1 mapping from every element to every matcher. To match a subset or superset of elements, you should use the `MatchElements` function with the `IgnoreExtras` and `IgnoreMissing` options. `IgnoreExtras` will ignore elements that don't map to a matcher, e.g. 2760 2761 ```go 2762 Expect(actual).To(MatchElements(id, IgnoreExtras, Elements{ 2763 "A": Not(BeZero()), 2764 "B": MatchRegexp("[A-Z]: [a-z ]+"), 2765 // Ignore lack of "C" in the matcher. 2766 })) 2767 ``` 2768 2769 `IgnoreMissing` will ignore matchers that don't map to an element, e.g. 2770 2771 ```go 2772 Expect(actual).To(MatchElements(id, IgnoreMissing, Elements{ 2773 "A": Not(BeZero()), 2774 "B": MatchRegexp("[A-Z]: [a-z ]+"), 2775 "C": ContainSubstring("end"), 2776 "D": Equal("bar"), // Ignored, since actual.D does not exist. 2777 })) 2778 ``` 2779 2780 You can also use the flag `AllowDuplicates` to permit multiple elements in your slice to map to a single key and matcher in your fields (this flag is not meaningful when applied to structs). 2781 2782 ```go 2783 everyElementID := func(element interface{}) string { 2784 return "a constant" // Every element will map to the same key in this case; you can group them into multiple keys, however. 2785 } 2786 Expect(actual).To(MatchElements(everyElementID, AllowDuplicates, Elements{ 2787 "a constant": ContainSubstring(": "), // Because every element passes this test 2788 })) 2789 Expect(actual).NotTo(MatchElements(everyElementID, AllowDuplicates, Elements{ 2790 "a constant": ContainSubstring("foo bar baz"), // Only the first element passes this test 2791 })) 2792 ``` 2793 2794 The options can be combined with the binary or: `IgnoreMissing|IgnoreExtras|AllowDuplicates`. 2795 2796 Additionally, `gstruct` provides `MatchAllElementsWithIndex` and `MatchElementsWithIndex` function for applying a matcher with index to each element, identified by an `IdentifierWithIndex` function. A helper function is also included with `gstruct` called `IndexIdentity` that provides the functionality of the just using the index as your identifier as seen below. 2797 2798 ```go 2799 actual := []string{ 2800 "A: foo bar baz", 2801 "B: once upon a time", 2802 "C: the end", 2803 } 2804 id := func(index int, _ interface{}) string { 2805 return strconv.Itoa(index) 2806 } 2807 Expect(actual).To(MatchAllElementsWithIndex(id, Elements{ 2808 "0": Not(BeZero()), 2809 "1": MatchRegexp("[A-Z]: [a-z ]+"), 2810 "2": ContainSubstring("end"), 2811 })) 2812 // IndexIdentity is a helper function equivalent to id in this example 2813 Expect(actual).To(MatchAllElementsWithIndex(IndexIdentity, Elements{ 2814 "0": Not(BeZero()), 2815 "1": MatchRegexp("[A-Z]: [a-z ]+"), 2816 "2": ContainSubstring("end"), 2817 })) 2818 ``` 2819 2820 The `WithIndex` variants take the same options as the other functions. 2821 2822 ### Testing type `map` 2823 2824 All of the `*Fields` functions and types have a corresponding definitions `*Keys` which can perform analogous tests against map types: 2825 2826 ```go 2827 actual := map[string]string{ 2828 "A": "correct", 2829 "B": "incorrect", 2830 } 2831 2832 // fails, because `actual` includes the key B 2833 Expect(actual).To(MatchAllKeys(Keys{ 2834 "A": Equal("correct"), 2835 })) 2836 2837 // passes 2838 Expect(actual).To(MatchAllKeys(Keys{ 2839 "A": Equal("correct"), 2840 "B": Equal("incorrect"), 2841 })) 2842 2843 // passes 2844 Expect(actual).To(MatchKeys(IgnoreMissing, Keys{ 2845 "A": Equal("correct"), 2846 "B": Equal("incorrect"), 2847 "C": Equal("whatever"), // ignored, because `actual` doesn't have this key 2848 })) 2849 ``` 2850 2851 ### Testing pointer values 2852 2853 `gstruct` provides the `PointTo` function to apply a matcher to the value pointed-to. It will fail if the pointer value is `nil`: 2854 2855 foo := 5 2856 Expect(&foo).To(PointTo(Equal(5))) 2857 var bar *int 2858 Expect(bar).NotTo(PointTo(BeNil())) 2859 2860 ### Putting it all together: testing complex structures 2861 2862 The `gstruct` matchers are intended to be composable, and can be combined to apply fuzzy-matching to large and deeply nested structures. The additional `Ignore()` and `Reject()` matchers are provided for ignoring (always succeed) fields and elements, or rejecting (always fail) fields and elements. 2863 2864 Example: 2865 2866 ```go 2867 coreID := func(element interface{}) string { 2868 return strconv.Itoa(element.(CoreStats).Index) 2869 } 2870 Expect(actual).To(MatchAllFields(Fields{ 2871 "Name": Ignore(), 2872 "StartTime": BeTemporally(">=", time.Now().Add(-100 * time.Hour)), 2873 "CPU": PointTo(MatchAllFields(Fields{ 2874 "Time": BeTemporally(">=", time.Now().Add(-time.Hour)), 2875 "UsageNanoCores": BeNumerically("~", 1E9, 1E8), 2876 "UsageCoreNanoSeconds": BeNumerically(">", 1E6), 2877 "Cores": MatchElements(coreID, IgnoreExtras, Elements{ 2878 "0": MatchAllFields(Fields{ 2879 Index: Ignore(), 2880 "UsageNanoCores": BeNumerically("<", 1E9), 2881 "UsageCoreNanoSeconds": BeNumerically(">", 1E5), 2882 }), 2883 "1": MatchAllFields(Fields{ 2884 Index: Ignore(), 2885 "UsageNanoCores": BeNumerically("<", 1E9), 2886 "UsageCoreNanoSeconds": BeNumerically(">", 1E5), 2887 }), 2888 }), 2889 })), 2890 "Memory": PointTo(MatchAllFields(Fields{ 2891 "Time": BeTemporally(">=", time.Now().Add(-time.Hour)), 2892 "AvailableBytes": BeZero(), 2893 "UsageBytes": BeNumerically(">", 5E6), 2894 "WorkingSetBytes": BeNumerically(">", 5E6), 2895 "RSSBytes": BeNumerically("<", 1E9), 2896 "PageFaults": BeNumerically("~", 1000, 100), 2897 "MajorPageFaults": BeNumerically("~", 100, 50), 2898 })), 2899 "Rootfs": m.Ignore(), 2900 "Logs": m.Ignore(), 2901 })) 2902 ``` 2903 2904 ## `gmeasure`: Benchmarking Code 2905 2906 `gmeasure` provides support for measuring and recording benchmarks of your code and tests. It can be used as a simple standalone benchmarking framework, or as part of your code's test suite. `gmeasure` integrates cleanly with Ginkgo V2 to enable rich benchmarking of code alongside your tests. 2907 2908 ### A Mental Model for `gmeasure` 2909 2910 `gmeasure` is organized around the metaphor of `Experiment`s that can each record multiple `Measurement`s. To use `gmeasure` you create a `NewExperiment` and use the resulting `experiment` object to record values and durations. You can then print out the `experiment` to get a report of all measurements or access specific measurements and their statistical aggregates to perform comparisons and/or make assertions. 2911 2912 An `experiment` can record _multiple_ `Measurement`s. Each `Measurement` has a `Name`, a `Type` (either `MeasurementTypeDuration` or `MeasurementTypeValue`), and a collection of recorded data points (of type `float64` for Value measurements and `time.Duration` for Duration measurements). In this way an experiment might describe a system or context being measured and can contain multiple measurements - one for each aspect of the system in question. 2913 2914 `Experiment`s can either record values and durations that the user passes in directly. Or they can invoke callbacks and accept their return values as Value data points, or measure their runtimes to compute Duration data points. `Experiment`s can also _sample_ callbacks, calling them repeatedly to get an ensemble of data points. 2915 2916 A `Measurement` is created when its first data point is recorded by an `Experiment`. Subsequent data points with the same measurement name are appended to the measurement: 2917 2918 ```go 2919 experiment := gmeasure.NewExperiment("My Experiment") 2920 experiment.RecordDuration("runtime", 3*time.Second) //creates a new Measurement called "runtime" 2921 experiment.RecordDuration("runtime", 5*time.Second) //appends a data point to "runtime" 2922 ``` 2923 2924 As described below, Measurements can be decorated with additional information. This includes information about the `Units` for the measurement, the `Precision` with which to render the measurement, and any `Style` to apply when rendering the measurement. Individual data points can also be decorated with an `Annotation` - an arbitrary string that is associated with that data point and gives it context. Decorations are applied as typed variadic arguments: 2925 2926 ```go 2927 experiment := gmeasure.NewExperiment("My Experiment") 2928 2929 // The first call to `RecordValue` for a measurement must set up any units, style, or precision decorations 2930 experiment.RecordValue("length", 3.141, gmeasure.Units("inches"), gmeasure.Style("{{blue}}"), gmeasure.Precision(2), gmeasure.Annotation("box A)")) 2931 2932 // Subsequent calls can attach an annotation. In this call a new data-point of `2.71` is added to the `length` measurement with the annotation `box B`. 2933 experiment.RecordValue("length", 2.71, gmeasure.Annotation("box B")) 2934 ``` 2935 2936 Once recorded, `Measurements` can be fetched from the `experiment` by name via `experiment.Get("name")`. The returned `Measurement` object includes all the data points. To get a statistical summary of the data points (that includes the min, max, median, mean, and standard deviation) call `measurement.Stats()` or `experiment.GetStats("name")`. These statistical summaries can also be rank-ordered with `RankStats()`. 2937 2938 `gmeasure` is designed to integrate with Ginkgo. This is done by registering `Experiment`s, `Measurement`s and `Ranking`s as `ReportEntry`s via Ginkgo's `AddReportEntry`. This will cause Ginkgo to emit nicely formatted and styled summaries of each of these objects when generating the test report. 2939 2940 Finally, `gmeasure` provides a mechanism to cache `Experiment`s to disk with a specified version number. This enables multiple use-cases. You can cache expensive experiments to avoid rerunning them while you iterate on other experiments. You can also compare experiments to cached experiments to explore whether changes in performance have been introduced to the codebase under test. 2941 2942 `gmeasure` includes detailed [godoc documentation](https://pkg.go.dev/github.com/onsi/gomega/gmeasure) - this narrative documentation is intended to help you get started with `gmeasure`. 2943 2944 ### Measuring Values 2945 2946 `Experiment`s can record arbitrary `float64` values. You can do this by directly providing a `float64` via `experiment.RecordValue(measurementName string, value float64, decorators ...interface{})` or by providing a callback that returns a float64 via `experiment.MeasureValue(measurementName string, callback func() float64, decorators ...interface{})`. 2947 2948 You can apply `Units`, `Style`, and `Precision` decorators to control the appearance of the `Measurement` when reports are generated. These decorators must be applied when the first data point is recorded but can be elided thereafter. You can also associate an `Annotation` decoration with any recorded data point. 2949 2950 `Experiment`s are thread-safe so you can call `RecordValue` and `MeasureValue` from any goroutine. 2951 2952 ### Measuring Durations 2953 2954 `Experiment`s can record arbitrary `time.Duration` durations. You can do this by directly providing a `time.Duration` via `experiment.RecordDuration(measurementName string, duration time.Duration, decorators ...interface{})` or by providing a callback via `experiment.MeasureDuration(measurementName string, callback func(), decorators ...interface{})`. `gmeasure` will run the callback and measure how long it takes to complete. 2955 2956 You can apply `Style` and `Precision` decorators to control the appearance of the `Measurement` when reports are generated. These decorators must be applied when the first data point is recorded but can be elided thereafter. You can also associate an `Annotation` decoration with any recorded data point. 2957 2958 `Experiment`s are thread-safe so you can call `RecordDuration` and `MeasureDuration` from any goroutine. 2959 2960 ### Sampling 2961 2962 `Experiment`s support sampling callback functions repeatedly to build an ensemble of data points. All the sampling methods are configured by passing in a `SamplingConfig`: 2963 2964 ```go 2965 type SamplingConfig struct { 2966 N int 2967 Duration time.Duration 2968 NumParallel int 2969 MinSamplingInterval time.Duration 2970 } 2971 ``` 2972 2973 Setting `SamplingConfig.N` limits the total number of samples to perform to `N`. Setting `SamplingConfig.Duration` limits the total time spent sampling to `Duration`. At least one of these fields must be set. If both are set then `gmeasure` will `sample` until the first limiting condition is met. Setting `SamplingConfig.MinSamplingInterval` causes `gmeasure` to wait until at least `MinSamplingInterval` has elapsed between subsequent samples. 2974 2975 By default, the `Experiment`'s sampling methods will run their callbacks serially within the calling goroutine. If `NumParallel` greater than `1`, however, the sampling methods will spin up `NumParallel` goroutines and farm the work among them. You cannot use `NumParallel` with `MinSamplingInterval`. 2976 2977 The basic sampling method is `experiment.Sample(callback func(idx int), samplingConfig SamplingConfig)`. This will call the callback function repeatedly, passing in an `idx` counter that increments between each call. The sampling will end based on the conditions provided in `SamplingConfig`. Note that `experiment.Sample` is not explicitly associated with a measurement. You can use `experiment.Sample` whenever you want to repeatedly invoke a callback up to a limit of `N` and/or `Duration`. You can then record arbitrarily many value or duration measurements in the body of the callback. 2978 2979 A common use-case, however, is to invoke a callback repeatedly to measure its duration or record its returned value and thereby generate an ensemble of data-points. This is supported via the `SampleX` family of methods built on top of `Sample`: 2980 2981 ```go 2982 experiment.SampleValue(measurementName string, callback func(idx int) float64, samplingConfig SamplingConfig, decorations ...interface{}) 2983 experiment.SampleDuration(measurementName string, callback func(idx int), samplingConfig SamplingConfig, decorations ...interface{}) 2984 experiment.SampleAnnotatedValue(measurementName string, callback func(idx int) (float64, Annotation), samplingConfig SamplingConfig, decorations ...interface{}) 2985 experiment.SampleAnnotatedDuration(measurementName string, callback func(idx int) Annotation, samplingConfig SamplingConfig, decorations ...interface{}) 2986 ``` 2987 2988 each of these will contribute data points to the `Measurement` with name `measurementName`. `SampleValue` records the `float64` values returned by its callback. `SampleDuration` times each invocation of its callback and records the measured duration. `SampleAnnotatedValue` and `SampleAnnotatedDuration` expect their callbacks to return `Annotation`s. These are attached to each generated data point. 2989 2990 All these methods take the same decorators as their corresponding `RecordX` methods. 2991 2992 ### Measuring Durations with `Stopwatch` 2993 2994 In addition to `RecordDuration` and `MeasureDuration`, `gmeasure` also provides a `Stopwatch`-based abstraction for recording durations. To motivate `Stopwatch` consider the following example. Let's say we want to measure the end-to-end performance of a web-server. Here's the code we'd like to measure: 2995 2996 ```go 2997 It("measures the end-to-end performance of the web-server", func() { 2998 model, err := client.Fetch("model-id-17") 2999 Expect(err).NotTo(HaveOccurred()) 3000 3001 err = model.ReticulateSpines() 3002 Expect(err).NotTo(HaveOccurred()) 3003 3004 Expect(client.Save(model)).To(Succeed()) 3005 3006 reticulatedModels, err := client.List("reticulated-models") 3007 Expect(err).NotTo(HaveOccurred()) 3008 Expect(reticulatedModels).To(ContainElement(model)) 3009 }) 3010 ``` 3011 3012 One approach would be to use `MeasureDuration`: 3013 3014 ```go 3015 It("measures the end-to-end performance of the web-server", func() { 3016 experiment := gmeasure.NewExperiment("end-to-end web-server performance") 3017 AddReportEntry(experiment.Name, experiment) 3018 3019 var model Model 3020 var err error 3021 experiment.MeasureDuration("fetch", func() { 3022 model, err = client.Fetch("model-id-17") 3023 }) 3024 Expect(err).NotTo(HaveOccurred()) 3025 3026 err = model.ReticulateSpines() 3027 Expect(err).NotTo(HaveOccurred()) 3028 3029 experiment.MeasureDuration("save", func() { 3030 Expect(client.Save(model)).To(Succeed()) 3031 }) 3032 3033 var reticulatedModels []Models 3034 experiment.MeasureDuration("list", func() { 3035 reticulatedModels, err = client.List("reticulated-models") 3036 }) 3037 Expect(err).NotTo(HaveOccurred()) 3038 Expect(reticulatedModels).To(ContainElement(model)) 3039 }) 3040 ``` 3041 3042 this... _works_. But all those closures and local variables make the test a bit harder to read. We can clean it up with a `Stopwatch`: 3043 3044 ```go 3045 It("measures the end-to-end performance of the web-server", func() { 3046 experiment := gmeasure.NewExperiment("end-to-end web-server performance") 3047 AddReportEntry(experiment.Name, experiment) 3048 3049 stopwatch := experiment.NewStopwatch() // start the stopwatch 3050 3051 model, err := client.Fetch("model-id-17") 3052 stopwatch.Record("fetch") // record the amount of time elapsed and store it in a Measurement named fetch 3053 Expect(err).NotTo(HaveOccurred()) 3054 3055 err = model.ReticulateSpines() 3056 Expect(err).NotTo(HaveOccurred()) 3057 3058 stopwatch.Reset() // reset the stopwatch 3059 Expect(client.Save(model)).To(Succeed()) 3060 stopwatch.Record("save").Reset() // record the amount of time elapsed since the last Reset and store it in a Measurement named save, then reset the stopwatch 3061 3062 reticulatedModels, err := client.List("reticulated-models") 3063 stopwatch.Record("list") 3064 Expect(err).NotTo(HaveOccurred()) 3065 Expect(reticulatedModels).To(ContainElement(model)) 3066 }) 3067 ``` 3068 3069 that's now much cleaner and easier to reason about. If we wanted to sample the server's performance concurrently we could now simply wrap the relevant code in an `experiment.Sample`: 3070 3071 ```go 3072 It("measures the end-to-end performance of the web-server", func() { 3073 experiment := gmeasure.NewExperiment("end-to-end web-server performance") 3074 AddReportEntry(experiment.Name, experiment) 3075 3076 experiment.Sample(func(idx int) { 3077 defer GinkgoRecover() // necessary since these will launch as goroutines and contain assertions 3078 stopwatch := experiment.NewStopwatch() // we make a new stopwatch for each sample. Experiments are threadsafe, but Stopwatches are not. 3079 3080 model, err := client.Fetch("model-id-17") 3081 stopwatch.Record("fetch") 3082 Expect(err).NotTo(HaveOccurred()) 3083 3084 err = model.ReticulateSpines() 3085 Expect(err).NotTo(HaveOccurred()) 3086 3087 stopwatch.Reset() 3088 Expect(client.Save(model)).To(Succeed()) 3089 stopwatch.Record("save").Reset() 3090 3091 reticulatedModels, err := client.List("reticulated-models") 3092 stopwatch.Record("list") 3093 Expect(err).NotTo(HaveOccurred()) 3094 Expect(reticulatedModels).To(ContainElement(model)) 3095 }, gmeasure.SamplingConfig{N:100, Duration:time.Minute, NumParallel:8}) 3096 }) 3097 ``` 3098 3099 Check out the [godoc documentation](https://pkg.go.dev/github.com/onsi/gomega/gmeasure#Stopwatch) for more details about `Stopwatch` including support for `Pause`ing and `Resume`ing the stopwatch. 3100 3101 ### Stats and Rankings: Comparing Measurements 3102 3103 Once you've recorded a few measurements you'll want to try to understand and interpret them. `gmeasure` allows you to quickly compute statistics for a given measurement. Consider the following example. Let's say we have two different ideas for how to implement a sorting algorithm and want to hone in on the algorithm with the shortest median runtime. We could run an experiment: 3104 3105 ```go 3106 It("identifies the fastest algorithm", func() { 3107 experiment := gmeasure.NewExperiment("dueling algorithms") 3108 AddReportEntry(experiment.Name, experiment) 3109 3110 experiment.SampleDuration("runtime: algorithm 1", func(_ int) { 3111 RunAlgorithm1() 3112 }, gmeasure.SamplingConfig{N:1000}) 3113 3114 experiment.SampleDuration("runtime: algorithm 2", func(_ int) { 3115 RunAlgorithm2() 3116 }, gmeasure.SamplingConfig{N:1000}) 3117 }) 3118 ``` 3119 3120 This will sample the two competing tables and print out a tabular representation of the resulting statistics. (Note - you don't need to use Ginkgo here, you could just use `gmeasure` in your code directly and then `fmt.Println` the `experiment` to get the tabular report). 3121 3122 We could compare the tables by eye manually - or ask `gmeasure` to pick the winning algorithm for us: 3123 3124 ```go 3125 It("identifies the fastest algorithm", func() { 3126 experiment := gmeasure.NewExperiment("dueling algorithms") 3127 AddReportEntry(experiment.Name, experiment) 3128 3129 experiment.SampleDuration("runtime: algorithm 1", func(_ int) { 3130 RunAlgorithm1() 3131 }, gmeasure.SamplingConfig{N:1000}) 3132 3133 experiment.SampleDuration("runtime: algorithm 2", func(_ int) { 3134 RunAlgorithm2() 3135 }, gmeasure.SamplingConfig{N:1000}) 3136 3137 ranking := gmeasure.RankStats(gmeasure.LowerMedianIsBetter, experiment.GetStats("runtime: algorithm 1"), experiment.GetStats("runtime: algorithm 2")) 3138 AddReportEntry("Ranking", ranking) 3139 }) 3140 ``` 3141 3142 This will now emit a ranking result that will highlight the winning algorithm (in this case, the algorithm with the lower Median). `RankStats` supports the following `RankingCriteria`: 3143 3144 - `LowerMeanIsBetter` 3145 - `HigherMeanIsBetter` 3146 - `LowerMedianIsBetter` 3147 - `HigherMedianIsBetter` 3148 - `LowerMinIsBetter` 3149 - `HigherMinIsBetter` 3150 - `LowerMaxIsBetter` 3151 - `HigherMaxIsBetter` 3152 3153 We can also inspect the statistics of the two algorithms programatically. `experiment.GetStats` returns a `Stats` object that provides access to the following `Stat`s: 3154 3155 - `StatMin` - the data point with the smallest value 3156 - `StatMax` - the data point with the highest values 3157 - `StatMedian` - the median data point 3158 - `StatMean` - the mean of all the data points 3159 - `StatStdDev` - the standard deviation of all the data points 3160 3161 `Stats` can represent either Value Measurements or Duration Measurements. When inspecting a Value Measurement you can pull out the requested `Stat` (say, `StatMedian`) via `stats.ValueFor(StatMedian)` - this returns a `float64`. When inspecting Duration Measurements you can fetch `time.Duration` statistics via `stats.DurationFor(StatX)`. For either type you can fetch an appropriately formatted string representation of the stat via `stats.StringFor(StatX)`. You can also get a `float64` for either type by calling `stats.FloatFor(StatX)` (this simply returns a `float64(time.Duration)` for Duration Measurements and can be useful when you need to do some math with the stats). 3162 3163 Going back to our dueling algorithms example. Lets say we find that Algorithm 2 is the winner with a median runtime of around 3 seconds - and we want to be alerted by a failing test should the winner ever change, or the median runtime vary substantially. We can do that by writing a few assertions: 3164 3165 ```go 3166 It("identifies the fastest algorithm", func() { 3167 experiment := gmeasure.NewExperiment("dueling algorithms") 3168 AddReportEntry(experiment.Name, experiment) 3169 3170 experiment.SampleDuration("runtime: algorithm 1", func(_ int) { 3171 RunAlgorithm1() 3172 }, gmeasure.SamplingConfig{N:1000}) 3173 3174 experiment.SampleDuration("runtime: algorithm 2", func(_ int) { 3175 RunAlgorithm2() 3176 }, gmeasure.SamplingConfig{N:1000}) 3177 3178 ranking := gmeasure.RankStats(gmeasure.LowerMedianIsBetter, experiment.GetStats("runtime: algorithm 1"), experiment.GetStats("runtime: algorithm 2")) 3179 AddReportEntry("Ranking", ranking) 3180 3181 //assert that algorithm 2 is the winner 3182 Expect(ranking.Winner().MeasurementName).To(Equal("runtime: algorithm 2")) 3183 3184 //assert that algorithm 2's median is within 0.5 seconds of 3 seconds 3185 Expect(experiment.GetStats("runtime: algorithm 2").DurationFor(gmeasure.StatMedian)).To(BeNumerically("~", 3*time.Second, 500*time.Millisecond)) 3186 }) 3187 ``` 3188 3189 ### Formatting Experiment and Measurement Output 3190 3191 `gmeasure` can produce formatted tabular output for `Experiment`s, `Measurement`s, and `Ranking`s. Each of these objects provides a `String()` method and a `ColorableString()` method. The `String()` method returns a string that does not include any styling tags whereas the `ColorableString()` method returns a string that includes Ginkgo's console styling tags (e.g. Ginkgo will render a string like `{{blue}}{{bold}}hello{{/}} there` as a bold blue "hello" followed by a default-styled " there"). `ColorableString()` is called for you automatically when you register any of these `gmeasure` objects as Ginkgo `ReportEntry`s. 3192 3193 When printing out `Experiment`s, `gmeasure` will produce a table whose columns correspond to the key statistics provided by `gmeasure.Stats` and whose rows are the various `Measurement`s recorded by the `Experiment`. Users can also record and emit notes - contextual information about the experiment - by calling `experiment.RecordNote(note string)`. Each note will get its own row in the table. 3194 3195 When printing out `Measurement`s, `gmeasure` will produce a table that includes _all_ the data points and annotations for the `Measurement`. 3196 3197 When printing out `Ranking`s, `gmeasure` will produce a table similar to the `Experiment` table with the `Measurement`s sorted by `RankingCriteria`. 3198 3199 Users can adjust a few aspects of `gmeasure`s output. This is done by providing decorators to the `Experiment` methods that record data points: 3200 3201 - `Units(string)` - the `Units` decorator allows you to associate a set of units with a measurement. Subsequent renderings of the measurement's name will include the units in `[]` square brackets. 3202 - `Precision(int or time.Duration)` - the `Precision` decorator controls the rendering of numerical information. For Value Measurements an `int` is used to express the number of decimal points to print. For example `Precision(3)` will render values with `fmt.Sprintf("%.3f", value)`. For Duration Measurements a `time.Duration` is used to round durations before rendering them. For example `Precision(time.Second)` will render durations via `duration.Round(time.Second).String()`. 3203 - `Style(string)` - the `Style` decorator allows you to associate a Ginkgo console style to a measurement. The measurement's row will be rendered with this style. For example `Style("{{green}}")` will emit a green row. 3204 3205 These formatting decorators **must** be applied to the _first_ data point recorded for a given Measurement (this is when the Measurement object is initialized and its style, precision, and units fields are populated). 3206 3207 Just to get concrete here's a fleshed out example that uses all the things: 3208 3209 ```go 3210 It("explores a complex object", func() { 3211 experiment := gmeasure.NewExperiment("exploring the encabulator") 3212 AddReportEntry(experiment.Name, experiment) 3213 3214 experiment.RecordNote("Encabulation Properties") 3215 experiment.Sample(func(idx int) { 3216 stopwatch := experiment.NewStopwatch() 3217 encabulator.Encabulate() 3218 stopwatch.Record("Encabulate Runtime", gmeasure.Style("{{green}}"), gmeasure.Precision(time.Millisecond)) 3219 3220 var m runtime.MemStats 3221 runtime.ReadMemStats(&m) 3222 experiment.RecordValue("Encabulate Memory Usage", float64(m.Alloc / 1024 / 1024), gmeasure.Style("{{red}}"), gmeasure.Precision(3), gmeasure.Units("MB"), gmeasure.Annotation(fmt.Sprintf("%d", idx))) 3223 }, gmeasure.SamplingConfig{N:1000, NumParallel:4}) 3224 3225 experiment.RecordNote("Encabulation Teardown") 3226 experiment.MeasureDuration("Teardown Runtime", func() { 3227 encabulator.Teardown() 3228 }, gmeasure.Style("{{yellow}}")) 3229 3230 memoryStats := experiment.GetStats("Encabulate Memory Usage") 3231 minMemory := memoryStats.ValueFor(gmeasure.StatMin) 3232 maxMemory := memoryStats.ValueFor(gmeasure.StatMax) 3233 Expect(maxMemory - minMemory).To(BeNumerically("<=", 10), "Should not see memory fluctuations exceeding 10 megabytes") 3234 }) 3235 ``` 3236 3237 ### Ginkgo Integration 3238 3239 The examples throughout this documentation have illustrated how `gmeasure` interoperates with Ginkgo. In short - you can emit output for `Experiment`, `Measurement`s, and `Ranking`s by registering them as Ginkgo `ReportEntry`s via `AddReportEntry()`. 3240 3241 This simple connection point ensures that the output is appropriately formatted and associated with the spec in question. It also ensures that Ginkgo's machine readable reports will include appropriately encoded versions of these `gmeasure` objects. So, for example, `ginkgo --json-report=report.json` will include JSON encoded `Experiment`s in `report.json` if you remember to `AddReportEntry` the `Experiment`s. 3242 3243 ### Caching Experiments 3244 3245 `gmeasure` supports caching experiments to local disk. Experiments can be stored and retrieved from the cache by name and version number. Caching allows you to skip rerunning expensive experiments and versioned caching allows you to bust the cache by incrementing the version number. Under the hood, the cache is simply a set of files in a directory. Each file contains a JSON encoded header with the experiment's name and version number followed by the JSON-encoded experiment. The various cache methods are documented over at [pkg.go.dev](https://pkg.go.dev/github.com/onsi/gomega/gmeasure#ExperimentCache). 3246 3247 Using an `ExperimentCache` with Ginkgo takes a little bit of wiring. Here's an example: 3248 3249 ```go 3250 const EXPERIMENT_VERSION = 1 //bump this to bust the cache and recompute _all_ experiments 3251 3252 Describe("some experiments", func() { 3253 var cache gmeasure.ExperimentCache 3254 var experiment *gmeasure.Experiment 3255 3256 BeforeEach(func() { 3257 cache = gmeasure.NewExperimentCache("./gmeasure-cache") 3258 name := CurrentSpecReport().LeafNodeText // we use the text in each It block as the name of the experiment 3259 experiment = cache.Load(name, EXPERIMENT_VERSION) // we try to load the experiment from the cache 3260 if experiment != nil { 3261 // we have a cache hit - report on the experiment and skip this test. 3262 AddReportEntry(experiment) 3263 Skip("cached") 3264 } 3265 //we have a cache miss, make a new experiment and proceed with the test. 3266 experiment = gmeasure.NewExperiment(name) 3267 AddReportEntry(experiment) 3268 }) 3269 3270 It("measures foo runtime", func() { 3271 experiment.SampleDuration("runtime", func() { 3272 //do stuff 3273 }, gmeasure.SamplingConfig{N:100}) 3274 }) 3275 3276 It("measures bar runtime", func() { 3277 experiment.SampleDuration("runtime", func() { 3278 //do stuff 3279 }, gmeasure.SamplingConfig{N:100}) 3280 }) 3281 3282 AfterEach(func() { 3283 // AfterEaches always run, even for tests that call `Skip`. So we make sure we aren't a skipped test then save the experiment to the cache 3284 if !CurrentSpecReport().State.Is(types.SpecStateSkipped) { 3285 cache.Save(experiment.Name, EXPERIMENT_VERSION, experiment) 3286 } 3287 }) 3288 }) 3289 ``` 3290 3291 this test will load the experiment from the cache if it's available or run the experiment and store it in the cache if it is not. Incrementing `EXPERIMENT_VERSION` will force all experiments to rerun. 3292 3293 Another usecase for `ExperimentCache` is to cache and commit experiments to source control for use as future baselines. Your code can assert that measurements are within a certain range of the stored baseline. For example: 3294 3295 ```go 3296 Describe("server performance", func() { 3297 It("ensures a performance regression has not been introduced", func() { 3298 // make an experiment 3299 experiment := gmeasure.NewExperiment("performance regression test") 3300 AddReportEntry(experiment.Name, experiment) 3301 3302 // measure the performance of one endpoint 3303 experiment.SampleDuration("fetching one", func() { 3304 model, err := client.Get("id-1") 3305 Expect(err).NotTo(HaveOccurred()) 3306 Expect(model.Id).To(Equal("id-1")) 3307 }, gmeasure.SamplingConfig{N:100}) 3308 3309 // measure the performance of another endpoint 3310 experiment.SampleDuration("listing", func() { 3311 models, err := client.List() 3312 Expect(err).NotTo(HaveOccurred()) 3313 Expect(models).To(HaveLen(30)) 3314 }, gmeasure.SamplingConfig{N:100}) 3315 3316 cache := gmeasure.NewExperimentCache("./gemasure-cache") 3317 baseline := cache.Load("performance regression test", 1) 3318 if baseline == nil { 3319 // this is the first run, let's store a baseline 3320 cache.Save("performacne regression test", 1, experiment) 3321 } else { 3322 for _, m := range []string{"fetching one", "listing"} { 3323 baselineStats := baseline.GetStats(m) 3324 currentStats := experiment.GetStats(m) 3325 3326 //make sure the mean of the current performance measure is within one standard deviation of the baseline 3327 Expect(currentStats.DurationFor(gmeasure.StatMean)).To(BeNumerically("~", baselineStats.DurationFor(gmeasure.StatsMean), baselineStats.DurationFor(gmeasure.StatsStdDev)), m) 3328 } 3329 } 3330 }) 3331 }) 3332 ``` 3333 3334 ## `gleak`: Finding Leaked Goroutines 3335 3336  3337 3338 The `gleak` package provides support for goroutine leak detection. 3339 3340 > **Please note:** gleak is an experimental new Gomega package. 3341 3342 ### Basics 3343 3344 Calling `Goroutines` returns information about all goroutines of a program at 3345 this moment. `Goroutines` typically gets invoked in the form of 3346 `Eventually(Goroutines).ShouldNot(...)`. Please note the missing `()` after 3347 `Goroutines`, as it must be called by `Eventually` and **not before it** with 3348 its results passed to `Eventually` only once. This does not preclude calling 3349 `Goroutines()`, such as for taking goroutines snapshots. 3350 3351 Leaked goroutines are then detected by using `gleak`'s `HaveLeaked` matcher on 3352 the goroutines information. `HaveLeaked` checks the actual list of goroutines 3353 against a built-in list of well-known runtime and testing framework goroutines, 3354 as well as against any optionally additional goroutines specifications passed to 3355 `HaveLeaked`. Good, and thus "non-leaky", Goroutines can be identified in 3356 multiple ways: such as by the name of a topmost function on a goroutine stack or 3357 a snapshot of goroutine information taken before a test. Non-leaky goroutines 3358 can also be identified using basically any Gomega matcher, with `HaveField` or 3359 `WithTransform` being highly useful in test-specific situations. 3360 3361 The `HaveLeaked` matcher _succeeds_ if it finds any goroutines that are neither 3362 in the integrated list of well-known goroutines nor in the optionally specified 3363 `HaveLeaked` arguments. In consequence, any _success_ of `HaveLeaked` actually 3364 is meant to be a _failure_, because of leaked goroutines. `HaveLeaked` is thus 3365 mostly used in combination with `ShouldNot` and `NotTo`/`ToNot`. 3366 3367 ### Testing for Goroutine Leaks 3368 3369 In its most simple form, just run a goroutine discovery with a leak check right 3370 _after_ each test in `AfterEach`: 3371 3372 > **Important:** always use `Goroutines` and not `Goroutines()` in the call to 3373 > `Eventually`. This ensures that the goroutine discovery is correctly done 3374 > repeatedly as needed and not just a single time before calling `Eventually`. 3375 3376 ```go 3377 AfterEach(func() { 3378 Eventually(Goroutines).ShouldNot(HaveLeaked()) 3379 }) 3380 ``` 3381 3382 Using `Eventually` instead of `Ω`/`Expect` has the benefit of retrying the leak 3383 check periodically until there is no leak or until a timeout occurs. This 3384 ensures that goroutines that are (still) in the process of winding down can 3385 correctly terminate without triggering false positives. Please refer to the 3386 [`Eventually`](#eventually) section for details on how to change the timeout 3387 interval (which defaults to 1s) and the polling interval (which defaults to 3388 10ms). 3389 3390 Please note that this simplest form of goroutine leak test can cause false 3391 positives in situations where a test suite or dependency module uses additional 3392 goroutines. This simple form only looks at all goroutines _after_ a test has run 3393 and filters out all _well-known_ "non-leaky" goroutines, such as goroutines from 3394 Go's runtime and the testing frameworks (such as Go's own testing package and 3395 Gomega). 3396 3397 ### Ginkgo -p 3398 3399 In case you intend to run multiple package tests in parallel using `ginkgo -p 3400 ...`, you'll need to update any existing `BeforeSuite` or add new `BeforeSuite`s 3401 in each of your packages. Calling `gleak.IgnoreGinkgoParallelClient` at the 3402 beginning of `BeforeSuite` ensures that `gleak` updates its internal ignore list 3403 to ignore a background goroutine related to the communication between Ginkgo and 3404 the parallel packages under test. 3405 3406 ```go 3407 var _ = BeforeSuite(func() { 3408 IgnoreGinkgoParallelClient() 3409 }) 3410 ``` 3411 3412 ### Using Goroutine Snapshots in Leak Testing 3413 3414 Often, it might be sufficient to cover for additional "non-leaky" goroutines by 3415 taking a "snapshot" of goroutines _before_ a test and then _afterwards_ use the 3416 snapshot to filter out the supposedly "non-leaky" goroutines. 3417 3418 Using Ginkgo's v2 `DeferCleanup` this can be expressed in a compact manner and 3419 without the need for explicitly declaring a variable to carry the list of known 3420 goroutines over from `BeforeEach` to `AfterEach`. This keeps all things declared 3421 neatly in a single place. 3422 3423 ```go 3424 BeforeEach(func() { 3425 goods := Goroutines() 3426 DeferCleanup(func() { 3427 Eventually(Goroutines).ShouldNot(HaveLeaked(goods)) 3428 }) 3429 }) 3430 ``` 3431 3432 ### `HaveLeaked` Matcher 3433 3434 ```go 3435 Eventually(ACTUAL).ShouldNot(HaveLeaked(NONLEAKY1, NONLEAKY2, NONLEAKY3, ...)) 3436 ``` 3437 3438 causes a test to fail if `ACTUAL` after filtering out the well-known "good" (and 3439 non-leaky) goroutines of the Go runtime and test frameworks, as well as 3440 filtering out the additional non-leaky goroutines passed to the matcher, still 3441 results in one or more goroutines. The ordering of the goroutines does not 3442 matter. 3443 3444 `HaveLeaked` always takes the built-in list of well-known good goroutines into 3445 consideration and this list can neither be overridden nor disabled. Additional 3446 known non-leaky goroutines `NONLEAKY1`, ... can be passed to `HaveLeaks` either 3447 in form of `GomegaMatcher`s or in shorthand notation: 3448 3449 - `"foo.bar"` is shorthand for `IgnoringTopFunction("foo.bar")` and filters out 3450 any (non-leaky) goroutine with its topmost function on the backtrace stack 3451 having the exact name `foo.bar`. 3452 3453 - `"foo.bar..."` is shorthand for `IgnoringTopFunction("foo.bar...")` and 3454 filters out any (non-leaky) goroutine with its topmost function on the 3455 backtrace stack beginning with the prefix `foo.bar.`; please notice the 3456 trailing `.` dot. 3457 3458 - `"foo.bar [chan receive]"` is shorthand for `IgnoringTopFunction("foo.bar 3459 [chan receive]")` and filters out any (non-leaky) goroutine where its topmost 3460 function on the backtrace stack has the exact name `foo.bar` _and_ the 3461 goroutine is in a state beginning with `chan receive`. 3462 3463 - `[]Goroutine` is shorthand for 3464 `IgnoringGoroutines(<SLICEOFGOROUTINES>)`: it filters out the specified 3465 goroutines, considering them to be non-leaky. The goroutines are identified by 3466 their [goroutine IDs](#goroutine-ids). 3467 3468 - `IgnoringInBacktrace("foo.bar.baz")` filters out any (non-leaky) goroutine 3469 with `foo.bar.baz` _anywhere_ in its backtrace. 3470 3471 - additionally, any other `GomegaMatcher` can be passed to `HaveLeaked()`, as 3472 long as this matcher can work on a passed-in actual value of type 3473 `Goroutine`. 3474 3475 ### Goroutine Matchers 3476 3477 The `gleak` packages comes with a set of predefined Goroutine matchers, to be 3478 used with `HaveLeaked`. If these matchers succeed (that is, they match on a 3479 certain `Goroutine`), then `HaveLeaked` considers the matched goroutine to be 3480 non-leaky. 3481 3482 #### IgnoringTopFunction(topfname string) 3483 3484 ```go 3485 Eventually(ACTUAL).ShouldNot(HaveLeaked(IgnoringTopFunction(TOPFNAME))) 3486 ``` 3487 3488 In its most basic form, `IgnoringTopFunction` succeeds if `ACTUAL` contains a 3489 goroutine where the name of the topmost function on its call stack (backtrace) 3490 is `TOPFNAME`, causing `HaveLeaked` to filter out the matched goroutine as 3491 non-leaky. Different forms of `TOPFNAME` describe different goroutine matching 3492 criteria: 3493 3494 - `"foo.bar"` matches only if a goroutine's topmost function has this exact name 3495 (ignoring any function parameters). 3496 - `"foo.bar..."` matches if a goroutine's topmost function name starts with the 3497 prefix `"foo.bar."`; it doesn't match `"foo.bar"` though. 3498 - `"foo.bar [state]"` matches if a goroutine's topmost function has this exact 3499 name and the goroutine's state begins with the specified state string. 3500 3501 `ACTUAL` must be an array or slice of `Goroutine`s. 3502 3503 #### IgnoringGoroutines(goroutines []Goroutine) 3504 3505 ```go 3506 Eventually(ACTUAL).ShouldNot(HaveLeaked(IgnoringGoroutines(GOROUTINES))) 3507 ``` 3508 3509 `IgnoringGoroutines` succeeds if `ACTUAL` contains one or more goroutines which 3510 are elements of `GOROUTINES`, causing `HaveLeaked` to filter out the matched 3511 goroutine(s) as non-leaky. `IgnoringGoroutines` compares goroutines by their 3512 `ID`s (see [Goroutine IDs](#gorotuine-ids) for background information). 3513 3514 `ACTUAL` must be an array or slice of `Goroutine`s. 3515 3516 #### IgnoringInBacktrace(fname string) 3517 3518 ```go 3519 Eventually(Goroutines).ShouldNot(HaveLeaked(IgnoringInBacktrace(FNAME))) 3520 ``` 3521 3522 `IgnoringInBacktrace` succeeds if `ACTUAL` contains a goroutine where `FNAME` is 3523 contained anywhere within its call stack (backtrace), causing `HaveLeaked` to 3524 filter out the matched goroutine as non-leaky. Please note that 3525 `IgnoringInBacktrace` uses a (somewhat lazy) `strings.Contains` to check for any 3526 occurrence of `FNAME` in backtraces. 3527 3528 `ACTUAL` must be an array or slice of `Goroutine`s. 3529 3530 #### IgnoringCreator(creatorname string) 3531 3532 ```go 3533 Eventually(Goroutines).ShouldNot(HaveLeaked(IgnoringCreator(CREATORNAME))) 3534 ``` 3535 3536 In its most basic form, `IgnoringCreator` succeeds if `ACTUAL` contains a 3537 goroutine where the name of the function creating the goroutine matches 3538 `CREATORNAME`, causing `HaveLeaked` to filter out the matched goroutine(s) as 3539 non-leaky. `IgnoringCreator` uses `==` for comparing the creator function name. 3540 3541 Different forms of `CREATORNAME` describe different goroutine matching 3542 criteria: 3543 3544 - `"foo.bar"` matches only if a goroutine's creator function has this exact name 3545 (ignoring any function parameters). 3546 - `"foo.bar..."` matches if a goroutine's creator function name starts with the 3547 prefix `"foo.bar."`; it doesn't match `"foo.bar"` though. 3548 3549 ### Adjusting Leaky Goroutine Reporting 3550 3551 When `HaveLeaked` finds leaked goroutines, `gleak` prints out a description of 3552 (only) the _leaked_ goroutines. This is different from panic output that 3553 contains backtraces of all goroutines. 3554 3555 However, `noleak`'s goroutine dump deliberately is not subject to Gomega's usual 3556 object rendition controls, such as `format.MaxLength` (see also [Adjusting 3557 Output](#adjusting-output)). 3558 3559 `noleak` will print leaked goroutine backtraces in a more compact form, with 3560 function calls and locations combined into single lines. Additionally, `noleak` 3561 defaults to reporting only the package plus file name and line number, but not 3562 the full file path. For instance: 3563 3564 main.foo.func1() at foo/bar.go:123 3565 3566 Setting `noleak.ReportFilenameWithPath` to `true` will instead report full 3567 source code file paths: 3568 3569 main.foo.func1() at /home/coolprojects/ohmyleak/mymodule/foo/bar.go:123 3570 3571 ### Well-Known Non-Leaky Goroutines 3572 3573 The well-known good (and therefore "non-leaky") goroutines are identified by the 3574 names of the topmost functions on their stacks (backtraces): 3575 3576 - signal handling: 3577 - `os/signal.signal_recv` and `os/signal.loop` (covering 3578 varying state), 3579 - as well as `runtime.ensureSigM`. 3580 - Go's built-in [`testing`](https://pkg.go.dev/testing) framework: 3581 - `testing.RunTests [chan receive]`, 3582 - `testing.(*T).Run [chan receive]`, 3583 - and `testing.(*T).Parallel [chan receive]`. 3584 - the [Ginkgo testing framework](https://onsi.github.io/ginkgo/): 3585 - `github.com/onsi/ginkgo/v2/internal.(*Suite).runNode` (including anonymous 3586 inner functions), 3587 - the anonymous inner functions of 3588 `github.com/onsi/ginkgo/v2/internal/interrupt_handler.(*InterruptHandler).registerForInterrupts`, 3589 - the creators `github.com/onsi/ginkgo/v2/internal.(*genericOutputInterceptor).ResumeIntercepting` and `github.com/onsi/ginkgo/v2/internal.(*genericOutputInterceptor).ResumeIntercepting...`, 3590 - the creator `github.com/onsi/ginkgo/v2/internal.RegisterForProgressSignal`, 3591 - and finally 3592 `github.com/onsi/ginkgo/internal/specrunner.(*SpecRunner).registerForInterrupts` 3593 (for v1 support). 3594 3595 Additionally, any goroutines with `runtime.ReadTrace` in their backtrace stack 3596 are also considered to be non-leaky. 3597 3598 ### Goroutine IDs 3599 3600 In order to detect goroutine identities, we use what is generally termed 3601 "goroutine IDs". These IDs appear in runtime stack dumps ("backtrace"). But … 3602 are these goroutine IDs even unambiguous? What are their "guarantees", if there 3603 are any at all? 3604 3605 First, Go's runtime code uses the identifier (and thus term) [`goid` for 3606 Goroutine 3607 IDs](https://github.com/golang/go/search?q=goidgen&unscoped_q=goidgen). Good to 3608 know in case you need to find your way around Go's runtime code. 3609 3610 Now, based on [Go's `goid` runtime allocation 3611 code](https://github.com/golang/go/blob/release-branch.go1.18/src/runtime/proc.go#L4130), 3612 goroutine IDs never get reused – unless you manage to make the 64bit "master 3613 counter" of the Go runtime scheduler to wrap around. However, not all goroutine 3614 IDs up to the largest one currently seen might ever be used, because as an 3615 optimization goroutine IDs are always assigned to Go's "P" processors for 3616 assignment to newly created "G" goroutines in batches of 16. In consequence, 3617 there may be gaps and later goroutines might have lower goroutine IDs if they 3618 get created from a different P. 3619 3620 Finally, there's [Scott Mansfield's blog post on Goroutine 3621 IDs](https://blog.sgmansfield.com/2015/12/goroutine-ids/). To sum up Scott's 3622 point of view: don't use goroutine IDs. He spells out good reasons for why you 3623 should not use them. Yet, logging, debugging and testing looks like a sane and 3624 solid exemption from his rule, not least `runtime.Stack` includes the `goids` 3625 for 3626 some reason. 3627 3628 ### Credits 3629 3630 The _Leakiee the gopher_ mascot clearly has been inspired by the Go gopher art 3631 work of [Renee French](http://reneefrench.blogspot.com/). 3632 3633 The `gleak` package was heavily inspired by Uber's fine 3634 [goleak](https://github.com/uber-go/goleak) goroutine leak detector package. 3635 While `goleak` can be used with Gomega and Ginkgo in a very specific form, it 3636 unfortunately was never designed to be (optionally) used with a matcher library 3637 to unlock the full potential of reasoning about leaky goroutines. In fact, the 3638 crucial element of discovering goroutines is kept internal in `goleak`. In 3639 consequence, Gomega's `gleak` package uses its own goroutine discovery and is 3640 explicitly designed to perfectly blend in with Gomega (and Ginkgo). 3641 3642 {% endraw %}