You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
new: make collection-related matchers Go 1.23 iterator aware
- new internal helper package for dealing with Go 1.23 iterators
via reflection; for Go versions before 1.23 this package provides
the same helper functions as stubs instead, shielding both the
matchers code base as well as their tests from any code that
otherwise would not build on pre-iterator versions. This allows
to keep new iterator-related matcher code and associated tests
inline, hopefully ensuring good maintainability.
- with the exception of ContainElements and ConsistOf, the other
iterator-aware matchers do not need to go through producing all
collection elements first in order to work on a slice of these
elements. Instead, they directly work on the collection elements
individually as their iterator produces them.
- BeEmpty: iter.Seq, iter.Seq2 w/ tests
- HaveLen: iter.Seq, iter.Seq2 w/ tests
- HaveEach: iter.Seq, iter.Seq2 w/ tests
- ContainElement: iter.Seq, iter.Seq2 w/ tests
- HaveExactElements: iter.Seq, iter.Seq2 w/ tests
- ContainElements: iter.Seq, iter.Seq2 w/ tests
- ConsistOf: iter.Seq, iter.Seq2 w/ test
- HaveKey: iter.Seq2 only w/ test
- HaveKeyWithValue: iter.Seq2 only w/ test
- updated documentation.
Signed-off-by: thediveo <thediveo@gmx.eu>
Copy file name to clipboardexpand all lines: docs/index.md
+47-12
Original file line number
Diff line number
Diff line change
@@ -690,6 +690,8 @@ A number of community-supported matchers have appeared as well. A list is maint
690
690
691
691
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.
692
692
693
+
When using Go toolchain of version 1.23 or later, certain matchers as documented below become iterator-aware, handling iterator functions with `iter.Seq` and `iter.Seq2`-like signatures as collections in the same way as array/slice/map.
694
+
693
695
### Asserting Equivalence
694
696
695
697
#### Equal(expected interface{})
@@ -1114,15 +1116,15 @@ It is an error for either `ACTUAL` or `EXPECTED` to be invalid YAML.
1114
1116
Ω(ACTUAL).Should(BeEmpty())
1115
1117
```
1116
1118
1117
-
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.
1119
+
succeeds if `ACTUAL` is, in fact, empty. `ACTUAL` must be of type `string`, `array`, `map`, `chan`, or `slice`. Starting with Go 1.23, `ACTUAL` can be also an iterator assignable to `iter.Seq` or `iter.Seq2`. It is an error for `ACTUAL` to have any other type.
1118
1120
1119
1121
#### HaveLen(count int)
1120
1122
1121
1123
```go
1122
1124
Ω(ACTUAL).Should(HaveLen(INT))
1123
1125
```
1124
1126
1125
-
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.
1127
+
succeeds if the length of `ACTUAL` is `INT`. `ACTUAL` must be of type `string`, `array`, `map`, `chan`, or `slice`. Starting with Go 1.23, `ACTUAL` can be also an iterator assignable to `iter.Seq` or `iter.Seq2`. It is an error for `ACTUAL` to have any other type.
1126
1128
1127
1129
#### HaveCap(count int)
1128
1130
@@ -1145,7 +1147,7 @@ or
1145
1147
```
1146
1148
1147
1149
1148
-
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!).
1150
+
succeeds if `ACTUAL` contains an element that equals `ELEMENT`. `ACTUAL` must be an `array`, `slice`, or `map`. Starting with Go 1.23, `ACTUAL` can be also an iterator assignable to `iter.Seq` or `iter.Seq2`. It is an error for it to have any other type. For `map`s `ContainElement` searches through the map's values and not the keys. Similarly, for an iterator assignable to `iter.Seq2``ContainElement` searches through the `v` elements of the produced (_, `v`) pairs.
1149
1151
1150
1152
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:
1151
1153
@@ -1176,6 +1178,34 @@ var findings map[int]string
Only in case of `iter.Seq2`-like iterators, the matching contained pairs can also be returned in the map referenced by the pointer. A (k, v) pair matches when it's "v" value matches.
Actual must be an `array`, `slice` or `map`. For maps, `ContainElements` matches against the `map`'s values.
1230
+
Actual must be an `array`, `slice` or `map`. Starting with Go 1.23, `ACTUAL` can be also an iterator assignable to `iter.Seq` or `iter.Seq2`. For maps, `ContainElements` matches against the `map`'s values. Similarly, for an iterator assignable to `iter.Seq2``ContainElements` searches through the `v` elements of the produced (_, `v`) pairs.
1201
1231
1202
1232
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
1203
1233
is the only element passed in to `ContainElements`:
@@ -1208,6 +1238,8 @@ is the only element passed in to `ContainElements`:
1208
1238
1209
1239
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.
1210
1240
1241
+
Starting with Go 1.23, you can also pass in an iterator assignable to `iter.Seq` (but not `iter.Seq2`) as the only element to `ConsistOf`.
1242
+
1211
1243
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.
1212
1244
1213
1245
#### BeElementOf(elements ...interface{})
@@ -1263,17 +1295,18 @@ By default `ConsistOf()` uses `Equal()` to match the elements, however custom ma
Actual must be an `array`, `slice` or `map`. For maps, `ConsistOf` matches against the `map`'s values.
1298
+
Actual must be an `array`, `slice` or `map`. Starting with Go 1.23, `ACTUAL` can be also an iterator assignable to `iter.Seq` or `iter.Seq2`. For maps, `ConsistOf` matches against the `map`'s values. Similarly, for an iterator assignable to `iter.Seq2``ContainElement` searches through the `v` elements of the produced (_, `v`) pairs.
1267
1299
1268
-
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
1269
-
is the only element passed in to `ConsistOf`:
1300
+
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 is the only element passed in to `ConsistOf`:
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.
1276
1307
1308
+
Starting with Go 1.23, you can also pass in an iterator assignable to `iter.Seq` (but not `iter.Seq2`) as the only element to `ConsistOf`.
`ACTUAL` must be an `array` or `slice`. Starting with Go 1.23, `ACTUAL` can be also an iterator assignable to `iter.Seq` (but not `iter.Seq2`).
1300
1333
1301
1334
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
1302
1335
is the only element passed in to `HaveExactElements`:
@@ -1313,9 +1346,9 @@ Note that Go's type system does not allow you to write this as `HaveExactElement
1313
1346
Ω(ACTUAL).Should(HaveEach(ELEMENT))
1314
1347
```
1315
1348
1316
-
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!).
1349
+
succeeds if `ACTUAL` solely consists of elements that equal `ELEMENT`. `ACTUAL` must be an `array`, `slice`, or `map`. For `map`s `HaveEach` searches through the map's values, not its keys. Starting with Go 1.23, `ACTUAL` can be also an iterator assignable to `iter.Seq` or `iter.Seq2`. For `iter.Seq2``HaveEach` searches through the `v` part of the yielded (_, `v`) pairs.
1317
1350
1318
-
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.
1351
+
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. Similar, an iterator not yielding any elements is also considered to be an error.
1319
1352
1320
1353
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:
1321
1354
@@ -1329,7 +1362,7 @@ By default `HaveEach()` uses the `Equal()` matcher under the hood to assert equa
1329
1362
Ω(ACTUAL).Should(HaveKey(KEY))
1330
1363
```
1331
1364
1332
-
succeeds if `ACTUAL` is a map with a key that equals `KEY`. It is an error for `ACTUAL` to not be a `map`.
1365
+
succeeds if `ACTUAL` is a map with a key that equals `KEY`. Starting with Go 1.23, `ACTUAL` can be also an iterator assignable to `iter.Seq2` and `HaveKey(KEY)` then succeeds if the iterator produces a (`KEY`, `_`) pair. It is an error for `ACTUAL` to have any other type than `map` or `iter.Seq2`.
1333
1366
1334
1367
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:
1335
1368
@@ -1343,14 +1376,16 @@ By default `HaveKey()` uses the `Equal()` matcher under the hood to assert equal
1343
1376
Ω(ACTUAL).Should(HaveKeyWithValue(KEY, VALUE))
1344
1377
```
1345
1378
1346
-
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`.
1379
+
succeeds if `ACTUAL` is a map with a key that equals `KEY` mapping to a value that equals `VALUE`. Starting with Go 1.23, `ACTUAL` can be also an iterator assignable to `iter.Seq2` and `HaveKeyWithValue(KEY)` then succeeds if the iterator produces a (`KEY`, `VALUE`) pair. It is an error for `ACTUAL` to have any other type than `map` or `iter.Seq2`.
1347
1380
1348
1381
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:
MatchError(MatchRegexp(`cannot return findings\. Need \*map\[int\]string, got \*map\[int\]interface`)))
404
+
})
405
+
})
406
+
})
407
+
408
+
Context("without any matches", func() {
409
+
When("the matcher did not error", func() {
410
+
It("should report non-match", func() {
411
+
varstashstring
412
+
rem:=ContainElement("barrz", &stash)
413
+
m, err:=rem.Match(universalIter)
414
+
Expect(m).To(BeFalse())
415
+
Expect(err).NotTo(HaveOccurred())
416
+
Expect(rem.FailureMessage(universalIter)).To(MatchRegexp(`Expected\n.+\nto contain element matching\n.+: barrz`))
417
+
418
+
varstashslice []string
419
+
rem=ContainElement("barrz", &stashslice)
420
+
m, err=rem.Match(universalIter)
421
+
Expect(m).To(BeFalse())
422
+
Expect(err).NotTo(HaveOccurred())
423
+
Expect(rem.FailureMessage(universalIter)).To(MatchRegexp(`Expected\n.+\nto contain element matching\n.+: barrz`))
424
+
})
425
+
})
426
+
427
+
When("the matcher errors", func() {
428
+
It("should report last matcher error", func() {
429
+
varstash []interface{}
430
+
Expect(ContainElement(HaveField("yeehaw", 42), &stash).Match(universalIter)).Error().To(MatchError(MatchRegexp(`HaveField encountered:\n.*<string>: baz\nWhich is not a struct`)))
expected:="Expected\n.*<func\\(func\\(string\\) bool\\)>:.*\nto have exact elements with\n.*\\[\"foo\", \"bar\", \"baz\", \"argh\"\\]\nthe missing elements start from index 3"
0 commit comments