diff --git a/docs/index.md b/docs/index.md index 903fa2cf8..eccde8c83 100644 --- a/docs/index.md +++ b/docs/index.md @@ -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 diff --git a/matchers/have_each_matcher.go b/matchers/have_each_matcher.go index 14eee470f..025b6e1ac 100644 --- a/matchers/have_each_matcher.go +++ b/matchers/have_each_matcher.go @@ -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() diff --git a/matchers/have_each_matcher_test.go b/matchers/have_each_matcher_test.go index 8beb3e80b..284d03e44 100644 --- a/matchers/have_each_matcher_test.go +++ b/matchers/have_each_matcher_test.go @@ -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)) @@ -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))) }) })