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

Support dynamic return values #742

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ type TestingT interface {
Call
*/

type returnArgumentsFunc func(args Arguments) Arguments

// Call represents a method call and is used for setting expectations,
// as well as recording activity.
type Call struct {
Expand All @@ -39,9 +41,15 @@ type Call struct {
Arguments Arguments

// Holds the arguments that should be returned when
// this method is called.
// this method is called. If the first and only value is
// function which takes and returns Arguments, that will be invoked
// on each call of the mock to determine what to return.
ReturnArguments Arguments

// if the first arg in ReturnArguments is a returnArgumentsFunc, this
// stores that ready for use
returnFunc returnArgumentsFunc

// Holds the caller info for the On() call
callerInfo []string

Expand Down Expand Up @@ -88,15 +96,44 @@ func (c *Call) unlock() {
c.Parent.mutex.Unlock()
}

// If the only return arg is a function which takes and returns Arguments, invoke it instead of returning it as the value
func (c *Call) getReturnArguments(args Arguments) Arguments {
if c.returnFunc != nil {
return c.returnFunc(args)
}

return c.ReturnArguments
}

var argumentsType = reflect.TypeOf(Arguments(nil))

// Return specifies the return arguments for the expectation.
//
// Mock.On("DoSomething").Return(errors.New("failed"))
//
// If you pass a single returnArg which is a function that itself takes Arguments and returns Arguments,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use different method. Mock is already not trivial to understand, each method should strive to do one thing and do it well.

// that will be invoked at runtime.
//
// Mock.On("HelloWorld").Return(func(args mock.Arguments) mock.Arguments { return "Hello " + arg[0].(string) })
func (c *Call) Return(returnArguments ...interface{}) *Call {
c.lock()
defer c.unlock()

c.ReturnArguments = returnArguments

if len(c.ReturnArguments) == 1 {
gburt marked this conversation as resolved.
Show resolved Hide resolved
fn := reflect.ValueOf(c.ReturnArguments[0])
if fn.Kind() == reflect.Func {
fnType := fn.Type()
if fnType.NumIn() == 1 && fnType.NumOut() == 1 && fnType.In(0) == argumentsType && fnType.Out(0) == argumentsType {
c.returnFunc = func(args Arguments) Arguments {
ret := fn.Call([]reflect.Value{reflect.ValueOf(args)})
return ret[0].Interface().(Arguments)
}
}
}
}

return c
}

Expand Down Expand Up @@ -393,7 +430,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
}

m.mutex.Lock()
returnArgs := call.ReturnArguments
returnArgs := call.getReturnArguments(arguments)
m.mutex.Unlock()

return returnArgs
Expand Down
15 changes: 15 additions & 0 deletions mock/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,21 @@ func Test_Mock_Return_Run_Out_Of_Order(t *testing.T) {
assert.NotNil(t, call.Run)
}

func Test_Mock_Return_Func(t *testing.T) {

// make a test impl object
var mockedService = new(TestExampleImplementation)

mockedService.On("TheExampleMethod", 1, 2, 3).
Return(func(args Arguments) Arguments {
return []interface{}{42, fmt.Errorf("hrm")}
}).
Once()

answer, _ := mockedService.TheExampleMethod(1, 2, 3)
assert.Equal(t, 42, answer)
}

func Test_Mock_Return_Once(t *testing.T) {

// make a test impl object
Expand Down