corbym / gocrest

GoCrest - Hamcrest-like matchers for Go
BSD 3-Clause "New" or "Revised" License
102 stars 6 forks source link

Implement the gomock.Matcher interface #4

Closed jpolack closed 3 years ago

jpolack commented 3 years ago

I was using gomock and wanted to use our matchers. Sadly it didn't work out because our matchers don't implement the gomock.Matcher interface:

type Matcher interface {
  // Matches returns whether x is a match.
  Matches(x interface{}) bool

  // String describes what the matcher matches.
  String() string
}

To implement the gomock.Matcher interface we would have to:

  1. implement a .Matches method as a struct method, not only as a field
  2. implement the Stringer interface

The Stringer interface should be fairly easy. But the Matches method would cause trouble, because Matcher already has a field called Matches of type func(actual interface{}) bool.

At this point I see two options:

1. add a mapping method from gocrest.Matcher to gomock.Matcher:

A quick win, but kind of dirty because it doesn't really take advantage of go's interface system. Additionally it kind of breaks gomocks syntax:

m.
  EXPECT().
  Bar(is.Equalto(99).toGomockMatcher()).
  Return(101)

2. rename the Matches method on the Matcher struct:

Seems like the cleaner way to me, but would cause a breaking change in how matchers are written. Would result in the following gomock syntax:

m.
  EXPECT().
  Bar(is.Equalto(99)).
  Return(101)

I would love to discuss this and find a way that suites us best!

corbym commented 3 years ago

this should not be as hard as you think tbh.

Simply adding:

func (matcher *Matcher) String() string {
    return matcher.Describe
}

.. to the Matcher's set of functions in matcher.go should make the matchers more functional.

The Matcher function declared in the gocrest.Matcher struct already conforms to the gomock interface, it just needs a way to conform fully to the Stringer interface (which is with the above code) to make the output readable.

(TLDR; in order to conform to an interface, a go struct only needs methods with the same name, params and return type as the interface. gocrest.Matcher already does this for the gomock interface.)

corbym commented 3 years ago

I have added the Stringer function, this should be fully compatible now. I have tried using gocrest matchers with gomock, and although I haven't included a test specifically, I did manage to get some reasonably ok output from the matcher:

=== RUN   TestFoo
    matcher_test.go:879: wrong number of arguments to Return for *mock_sample.MockIndex.Anon: got 1, want 0 [/Users/mcorbyeaglen/repos/gocrest/matcher_test.go:878]
    panic.go:617: missing call(s) to *mock_sample.MockIndex.Anon(is equal to value equal to <99>) /Users/mcorbyeaglen/repos/gocrest/matcher_test.go:878
    panic.go:617: aborting test due to missing call(s)
--- FAIL: TestFoo (0.00s)