-
Notifications
You must be signed in to change notification settings - Fork 1.7k
mock: [feature] dynamic return values #1726
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
base: master
Are you sure you want to change the base?
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
@@ -29,7 +29,7 @@ type TestExampleImplementation struct { | |||
|
|||
func (i *TestExampleImplementation) TheExampleMethod(a, b, c int) (int, error) { | |||
args := i.Called(a, b, c) | |||
return args.Int(0), errors.New("Whoops") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looked wrong -- not sure why this would always return an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
RunWithReturn
which is essentiallyRun
but addsArguments
as the return type and propagates them.Used this as a starting point #742
Changes
RunWithReturn
Motivation
Helps write simple tests that can dynamically calculate return values.
Example (from test)
Related issues
#742