onsi / gomega

Ginkgo's Preferred Matcher Library

Home Page:http://onsi.github.io/gomega/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is there any support for matching ordered slice?

rickyson96 opened this issue · comments

Hi! Thanks for the awesome matcher package!

I know that currently we can use Equal and friends to have exact matching of ordered slice.
But what if I want to match the slice, in an ordered manner, with custom equality matcher?

Something like this

tc := []string{"aaa", "bbb", "ccc"}
Expect(tc).To(ConsistOf(
  ContainsSubstring("a"), // "aaa"
  Equal("bbb"), // "bbb"
  BeEquivalentTo("ccc"), // "ccc"
))

My current use case is that I have list of proto message which I can't use exact equality. I'm using the gproto package to do the equality. Something similar to the snippet below.

tc := []proto.Message{...}
expectation := []proto.Message{...}
matchers := []types.GomegaMatcher{}
for _, ex := range expectations {
  matchers := append(matchers, gproto.Equal(ex))
}
Expect(tc).To(ConsistOf(matchers)) // but no ordering

Currently, I'm using loop to match the index with matcher's index to get the ordering. Is it expected that way or is there something I am missing?

hey @rickyson96 -- you're right (and I'm somewhat surprised, all these years later!) that we still don't have a simple matcher matcher to perform an ordered per-element assertion on a slice/array. If you'd be interested in working on a PR that would be wonderful!

In the meantime, you can use the gstruct package to solve for this problem - though there's a bit more complexity involved. You'll want to do something like:

import "github.com/onsi/gomega/gstruct"


var _ = It("is the correct, ordered, set of messages", func() {
    tc := []proto.Message{...}
    expectation := []proto.Message{...}

    // gstruct.Elements is a map[string]GomegaMatcher
    elements := gstruct.Elements{}
    for idx, ex := range expectations {
        elements[strconv.Itoa(idx)] = gproto.Equal(ex) //note the index is used as a string key
    }

    //gstruct.MatchAllElements will iterate over tc and  use gstruct.IndexIdentity to get the key for each element (in this case, a string version of the index).  It will then look up the matcher from elements and match it against the value in tc.
    Expect(tc).To(gstruct.MatchAllElementsWithIndex(gstruct.IndexIdentity, elements))
})

this will give you an ordered assertion (and will fail if there are extra things in tc and/or missing things in tc). You can learn more in the gstruct docs here

Wow, I didn't think of the gstruct solution despite already using it for struct and maps.
Thanks for the information!