Add .Unset method to mock (#982)

* Add .Off method to mock

* Update README.md

* Update mock.go

* Update mock_test.go

* Update README.md

* Fix tests

* Add unset test

* remove prints

* fix test

* update readme
pull/689/merge
Paul Dufour 2022-06-22 10:31:35 +01:00 committed by GitHub
parent c31ea0312f
commit ba1076d8b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 143 additions and 0 deletions

View File

@ -190,6 +190,31 @@ func TestSomethingWithPlaceholder(t *testing.T) {
}
// TestSomethingElse2 is a third example that shows how you can use
// the Unset method to cleanup handlers and then add new ones.
func TestSomethingElse2(t *testing.T) {
// create an instance of our test object
testObj := new(MyMockedObject)
// setup expectations with a placeholder in the argument list
mockCall := testObj.On("DoSomething", mock.Anything).Return(true, nil)
// call the code we are testing
targetFuncThatDoesSomethingWithObj(testObj)
// assert that the expectations were met
testObj.AssertExpectations(t)
// remove the handler now so we can add another one that takes precedence
mockCall.Unset()
// return false now instead of true
testObj.On("DoSomething", mock.Anything).Return(false, nil)
testObj.AssertExpectations(t)
}
```
For more information on how to write mock code, check out the [API documentation for the `mock` package](http://godoc.org/github.com/stretchr/testify/mock).

View File

@ -199,6 +199,43 @@ func (c *Call) On(methodName string, arguments ...interface{}) *Call {
return c.Parent.On(methodName, arguments...)
}
// Unset removes a mock handler from being called.
// test.On("func", mock.Anything).Unset()
func (c *Call) Unset() *Call {
var unlockOnce sync.Once
for _, arg := range c.Arguments {
if v := reflect.ValueOf(arg); v.Kind() == reflect.Func {
panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg))
}
}
c.lock()
defer unlockOnce.Do(c.unlock)
foundMatchingCall := false
for i, call := range c.Parent.ExpectedCalls {
if call.Method == c.Method {
_, diffCount := call.Arguments.Diff(c.Arguments)
if diffCount == 0 {
foundMatchingCall = true
// Remove from ExpectedCalls
c.Parent.ExpectedCalls = append(c.Parent.ExpectedCalls[:i], c.Parent.ExpectedCalls[i+1:]...)
}
}
}
if !foundMatchingCall {
unlockOnce.Do(c.unlock)
c.Parent.fail("\n\nmock: Could not find expected call\n-----------------------------\n\n%s\n\n",
callString(c.Method, c.Arguments, true),
)
}
return c
}
// Mock is the workhorse used to track activity on another object.
// For an example of its usage, refer to the "Example Usage" section at the top
// of this document.

View File

@ -462,6 +462,87 @@ func Test_Mock_On_WithFuncTypeArg(t *testing.T) {
})
}
func Test_Mock_Unset(t *testing.T) {
// make a test impl object
var mockedService = new(TestExampleImplementation)
call := mockedService.
On("TheExampleMethodFuncType", "argA").
Return("blah")
found, foundCall := mockedService.findExpectedCall("TheExampleMethodFuncType", "argA")
require.NotEqual(t, -1, found)
require.Equal(t, foundCall, call)
call.Unset()
found, foundCall = mockedService.findExpectedCall("TheExampleMethodFuncType", "argA")
require.Equal(t, -1, found)
var expectedCall *Call
require.Equal(t, expectedCall, foundCall)
fn := func(string) error { return nil }
assert.Panics(t, func() {
mockedService.TheExampleMethodFuncType(fn)
})
}
// Since every time you call On it creates a new object
// the last time you call Unset it will only unset the last call
func Test_Mock_Chained_UnsetOnlyUnsetsLastCall(t *testing.T) {
// make a test impl object
var mockedService = new(TestExampleImplementation)
// determine our current line number so we can assert the expected calls callerInfo properly
_, _, line, _ := runtime.Caller(0)
mockedService.
On("TheExampleMethod1", 1, 1).
Return(0).
On("TheExampleMethod2", 2, 2).
On("TheExampleMethod3", 3, 3, 3).
Return(nil).
Unset()
expectedCalls := []*Call{
{
Parent: &mockedService.Mock,
Method: "TheExampleMethod1",
Arguments: []interface{}{1, 1},
ReturnArguments: []interface{}{0},
callerInfo: []string{fmt.Sprintf("mock_test.go:%d", line+2)},
},
{
Parent: &mockedService.Mock,
Method: "TheExampleMethod2",
Arguments: []interface{}{2, 2},
ReturnArguments: []interface{}{},
callerInfo: []string{fmt.Sprintf("mock_test.go:%d", line+4)},
},
}
assert.Equal(t, 2, len(expectedCalls))
assert.Equal(t, expectedCalls, mockedService.ExpectedCalls)
}
func Test_Mock_UnsetIfAlreadyUnsetFails(t *testing.T) {
// make a test impl object
var mockedService = new(TestExampleImplementation)
mock1 := mockedService.
On("TheExampleMethod1", 1, 1).
Return(1)
assert.Equal(t, 1, len(mockedService.ExpectedCalls))
mock1.Unset()
assert.Equal(t, 0, len(mockedService.ExpectedCalls))
assert.Panics(t, func() {
mock1.Unset()
})
assert.Equal(t, 0, len(mockedService.ExpectedCalls))
}
func Test_Mock_Return(t *testing.T) {
// make a test impl object