mock: [feature] dynamic return values#1726
Conversation
Co-authored-by: Bracken <abdawson@gmail.com>
The comments for the require package were just copied over from the assert package when generating the functions. This could lead to confusion because 1. The code-examples were showing examples using the assert package instead of the require package 2. The function-documentation was not mentioning that the functions were calling `t.FailNow()` which is some critical information when using this package.
brackendawson
left a comment
There was a problem hiding this comment.
This should override any previous calls to Return but also be overridden by any subsequent call to Return:
package kata_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
type myMock struct {
mock.Mock
}
func (m *myMock) Do() string {
return m.Called().String(0)
}
func TestIfy(t *testing.T) {
m := &myMock{}
m.On("Do").Return("one").Return("two")
assert.Equal(t, "two", m.Do())
m = &myMock{}
m.On("Do").Return("one").RunWithReturn(func(args mock.Arguments) mock.Arguments {
return mock.Arguments{"two"}
})
assert.Equal(t, "two", m.Do())
m = &myMock{}
m.On("Do").RunWithReturn(func(args mock.Arguments) mock.Arguments {
return mock.Arguments{"one"}
}).Return("two")
assert.Equal(t, "two", m.Do())
}This is why I suggested calling it ReturnFn, so that folks understand its purpose to to set return values. The purpose of Run is to implement side effects.
brackendawson
left a comment
There was a problem hiding this comment.
Good work so far. 👍
This also causes Mock.Unset to misbehave:
m = &myMock{}
m.On("Do").ReturnFn(func(args mock.Arguments) mock.Arguments { return mock.Arguments{"one"} })
m.On("Do").Return("two")
m.On("Do").Return("two").Unset() // Unset is an awful Method
assert.Equal(t, "one", m.Do())Mock.Unset is using Arguments.Diff to find matching calls, but because Arguments is empty it implicitly matches. Unset should probably never match any Call when Call.ReturnArguments is empty and Call.returnFn is non-nil. Unset should probably panic when called on a Call when Call.ReturnArguments is empty and Call.returnFn is non-nil, this is because functions are not comparable.
| // ReturnFn sets a handler to be called before returning. | ||
| // | ||
| // Mock.On("MyMethod", arg1, arg2).ReturnFn(func(args Arguments) Arguments { | ||
| // return Arguments{args.Get(0) + args.Get(1)} |
There was a problem hiding this comment.
The example should be valid Go, did you mean Int rather than Get?
| // return Arguments{args.Get(0) + args.Get(1)} | |
| // return Arguments{args.Int(0) + args.Int(1)} |
| returnFn := call.returnFn | ||
| m.mutex.Unlock() | ||
|
|
||
| if returnFn != nil { |
There was a problem hiding this comment.
Because call.ReturnArguments is exported it should probably be checked by this condition rather than returnFn, eg:
m = &myMock{}
m.On("Do").ReturnFn(func(args mock.Arguments) mock.Arguments { return mock.Arguments{"two"} })
m.ExpectedCalls[0].ReturnArguments = mock.Arguments{"one"}
assert.Equal(t, "one", m.Do())ie. Call.ReturnArguments should always override Call.returnFn
Summary
Adds
RunWithReturnwhich is essentiallyRunbut addsArgumentsas the return type and propagates them.Used this as a starting point #742
Changes
RunWithReturnMotivation
Helps write simple tests that can dynamically calculate return values.
Example (from test)
Related issues
#742