Skip to content

Commit

Permalink
make HaveEach error in case of nil/empty actual val (#524)
Browse files Browse the repository at this point in the history
- HaveEachMatcher errors for empty actual value
- update unit tests
- update documentation and include example when empty actuals are acceptable
  • Loading branch information
thediveo committed Feb 28, 2022
1 parent 9fc2ae2 commit c8ba582
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 6 deletions.
2 changes: 2 additions & 0 deletions docs/index.md
Expand Up @@ -978,6 +978,8 @@ Note that Go's type system does not allow you to write this as `ConsistOf([]stri

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!).

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.

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:

```go
Expand Down
5 changes: 5 additions & 0 deletions matchers/have_each_matcher.go
Expand Up @@ -23,6 +23,11 @@ func (matcher *HaveEachMatcher) Match(actual interface{}) (success bool, err err
}

value := reflect.ValueOf(actual)
if value.Len() == 0 {
return false, fmt.Errorf("HaveEach matcher expects a non-empty array/slice/map. Got:\n%s",
format.Object(actual, 1))
}

var valueAt func(int) interface{}
if isMap(actual) {
keys := value.MapKeys()
Expand Down
22 changes: 16 additions & 6 deletions matchers/have_each_matcher_test.go
Expand Up @@ -10,8 +10,6 @@ var _ = Describe("HaveEach", func() {
When("passed a supported type", func() {
Context("and expecting a non-matcher", func() {
It("should do the right thing", func() {
Expect([]int{}).Should(HaveEach(42))

Expect([2]int{2, 2}).Should(HaveEach(2))
Expect([2]int{2, 3}).ShouldNot(HaveEach(3))

Expand Down Expand Up @@ -57,13 +55,25 @@ var _ = Describe("HaveEach", func() {
})
})

When("passed a correctly typed nil", func() {
It("should operate succesfully on the passed in value", func() {
When("passed an empty supported type or correctly typed nil", func() {
It("should error", func() {
success, err := (&HaveEachMatcher{Element: []int{}}).Match(42)
Expect(success).Should(BeFalse())
Expect(err).Should(HaveOccurred())

var nilSlice []int
Expect(nilSlice).Should(HaveEach(1))
success, err = (&HaveEachMatcher{Element: nilSlice}).Match(1)
Expect(success).Should(BeFalse())
Expect(err).Should(HaveOccurred())

var nilMap map[int]string
Expect(nilMap).Should(HaveEach("foo"))
success, err = (&HaveEachMatcher{Element: nilMap}).Match(1)
Expect(success).Should(BeFalse())
Expect(err).Should(HaveOccurred())

// again, we eat our own documentation food here...
Expect([]int{}).To(Or(BeEmpty(), HaveEach(42)))
Expect([]int{1}).NotTo(Or(BeEmpty(), HaveEach(42)))
})
})

Expand Down

0 comments on commit c8ba582

Please sign in to comment.