golang / mock

GoMock is a mocking framework for the Go programming language.
Apache License 2.0
9.32k stars 607 forks source link

Can't use mocks when recieved input has random fields. #43

Closed noibar closed 7 years ago

noibar commented 8 years ago

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.

euank commented 8 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.

awe76 commented 2 years ago

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)
}