Closed noibar closed 7 years ago
Additional matchers don't have to be part of the gomock project.
If you implement the StrMatcher
struct in your own code and ensure it conforms to the gomock.Matcher
interface, then this code will notice that it conforms to said interface and use it.
For example, I once wanted to just match a small number of known fields of an http request, (just method and url) since those resulted in a more readable test (and the Date
field is effectively random). I wrote this matcher and used it like this.
By writing sufficiently complicated matchers, it is quite possible to match on partially random objects. In addition, using gomock.Any
combined with .Do
, and then putting logic in that function and asserting can be a powerful pattern and solve similar problems.
May I propose some tiny but very useful matcher?
// doMatch keeps state of the custom lambda matcher.
// match is a lambda function that asserts actual value matching.
// x keeps actual value.
type doMatch[V any] struct {
match func(v V) bool
x any
}
// DoMatch creates lambda matcher instance equipped with
// lambda function to detect if actual value matches
// some arbitrary criteria.
// Lambda matcher implements gomock customer matcher
// interface https://github.com/golang/mock/blob/5b455625bd2c8ffbcc0de6a0873f864ba3820904/gomock/matchers.go#L25.
// Sample of usage:
//
// mock.EXPECT().Foo(gomock.All(
//
// DoMatch(func(v Bar) {
// v.Greeting == "Hello world"
// }),
//
// ))
func DoMatch[V any](m func(v V) bool) gomock.Matcher {
return &doMatch[V]{
match: m,
}
}
// Matches receives actual value x casts it to specific type defined as a type parameter V
// and calls labmda function 'match' to resolve if x matches or not.
func (o *doMatch[V]) Matches(x any) bool {
o.x = x
v, ok := x.(V)
if !ok {
return false
}
return o.match(v)
}
// String describes what matcher matches.
func (o *doMatch[V]) String() string {
return fmt.Sprintf("is matched to %v", o.x)
}
you can't use EXPECT() for functions that recieve a complex object with random values. Other then that, objects that hold fields that are irellevant for the matching can't be matched with the given matchers. (I hope you don't mind the exessive tautology :))
In order to solve this, an additional matcher can be implemented. I thought to implement a StrMatcher, that will match between the objects String(). This way, if you want to ignore some of the fields in the matching process, you can implement a String() function that returns a description of all the object's interesing fields.