Closed clawconduce closed 8 years ago
See willfaught/gockle#1 for the background.
I was toying around with how it would look to have something like this. Normally, for a vararg func, you can only specify mock.Any or an entire concrete slice value. Something like:
m.When("F", mock.Any)
m.When("F", []interface{}{"a", "test"})
but when you want to assert concrete values for some varargs and mock.Any for others, you're stuck using a verbose mock.AnyIf like this:
// Match 2 varargs where the first is anything and the second is "test"
m.When("F", mock.AnyIf(func(v interface{}) bool {
var s, ok = v.([]interface{})
if !ok || len(s) != 2 {
return false
}
return s[1] == "test"
}))
Wouldn't it be nice if you could do something like this:
m.When("F", mock.Varargs{mock.Any, "test"})
But that got me thinking that this is really just a special case of the more general problem of wanting to nest mock.Any inside any slice, like this:
m.When("G", mock.Slice{mock.Any, "test"}, 123, mock.Slice{"a", mock.Any}) // Last mock.Slice is the varargs, or it could be a normal slice param
So basically this could all be accomplished with mock.AnyIf shorthands for nested mock variables in slices. I imagine the same for maps or channels might also be useful.
And to address @clawconduce's point about only wanting to specify a prefix of the slice, you could put some mock.Rest value at the end of the mock.Slice values as he detailed.
Something like
type Slice []interface{}
type Map map[interface{}]interface{}
type Chan []interface{}
with nested matching logic in Mock.find.
As @clawconduce this is an issue we don't have a good solution yet, as @willfaught comments, we use just one argument and write this as a mock.Any
or as a []interface{}{...}
But if you want to return variables as arguments, it might be a little more difficult to do, one thing that has worked for me is the use of the Call
method:
// Scan is the mock implementation
func (m *Query) Scan(dest ...interface{}) error {
ret := m.Called(dest)
ret0, _ := ret.Get(0).(error)
return ret0
}
// NewQueryScan returns a *Query with Scan mocked returning the args that we want.
func NewQueryScan(args ...interface{}) *Query {
query := &Query{}
query.When("Scan", mock.Any).Call(func(out ...interface{}) error {
for i := range args {
reflect.ValueOf(out[i]).Elem().Set(reflect.ValueOf(args[i]))
}
return nil
})
return query
}
As in Go you cannot have two methods with the same name but different number of arguments, I will probably remove the restriction on the number on arguments, so you don't need the mock.Any there.
PR maraino/go-mock/pull/7 solves this.
Cool! Thanks everyone!
I have some use cases for database queries where I want any database query to return an error. The number of arguments changes, so the
Any*
variables do not work.My thought was to add a
AnyZeroOrMoreArgs
(the name should be improved) that would break out of this loop: https://github.com/maraino/go-mock/blob/master/mock.go#L287 along with adjust other required checks. IfAnyZeroOrMoreArgs
was the only arg passed in to a mock, then it would match any call to a function regardless of how many args are passed in. TheAnyZeroOrMoreArgs
could also be passed in after specifying some number of args. So for example, if your code takes in a formatting string & a variable number of args after that, you could pass inAnythingOfType("string"), AnymoreArgs
.