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

Consider Generic Matchers #620

Open
therealmitchconnors opened this issue Jan 17, 2023 · 1 comment
Open

Consider Generic Matchers #620

therealmitchconnors opened this issue Jan 17, 2023 · 1 comment

Comments

@therealmitchconnors
Copy link

Have you considered adding generics support to gomega? I work with nested structures a lot in tests, and reflection-based checks are verbose, and may not work for types not easily castable from interface. Something like:

type Object struct {
  Owner Object
}

func getObjects() []Object {...}

func TestSomething(t *testing.T) {
  g := NewGomegaWithT(t)
  objs := getObjects()
  g.Expect(objs).To(HaveEach(
    func (x Object) bool {
      return x.Owner == nil
    }
  ))
}
@onsi
Copy link
Owner

onsi commented Jan 18, 2023

I and others have explored using generics a bit - and while the compile-time feedback is certainly helpful, in practice I find that it doesn't solve as many problems and can make composition a bit harder.

Instead, Gomega has fully embraced reflection and been evolving towards some pretty nifty composition. As a concrete example, the test in your example can be written in the following ways:

type Object struct {
  Owner OtherObject
}

func getObjects() []Object {...}

// simple composition of matchers using `HaveField` and `BeNil`
func TestSomethingViaHaveField(t *testing.T) {
  g := NewGomegaWithT(t)
  g.Expect(getObjects()).To(HaveEach(HaveField("Owner", BeNil()))
}


//overkill for this example, but custom matchers are quite easy with `gcustom`
HaveNilOwner := gcustom.MakeMatcher(func(object Object) (bool, err) {
  return object.Owner == nil, nil
}).WithMessage("have nil owner)

func TestSomethingViaCustomMatcher(t *testing.T) {
  g := NewGomegaWithT(t)
  g.Expect(getObjects()).To(HaveEach(HaveNilOwner))
}

Note that in the gcustom example above, Gomega does the type assertions for you and will guarantee that your matcher function always receives an object of type Object (otherwise a clear error is emitted).

You can learn more about gcustom here and more about HaveField here. Finally, there are some patterns for building complex matchers via composition here - though the introduction of gcustom does make a lot of this stuff much easier now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants