Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make HaveEach error in case of nil/empty actual val #524

Merged
merged 1 commit into from Feb 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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