You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Go Playground is timing out on build when github.com/stretchr/testify/mock is used (likely due to dependencies downloading), so submitted #1622 with tests to reproduce.
Only the call(s) with the exact matched arguments should be unset/removed.
Ideally only the call that Unset is invoked against is removed but the current API is written in a manner that requires a Call to unset so unless the call is tracked (e.g., c := mockService.On(....)), On must be called first then Unset which adds the expectation only to immediately remove it along with any other "matched" expectations. #982 which added Unset actually started with Unset on Mock but then moved it to Call to support Unset on a Call but possibly it could have considered leaving Unset(methodName string, arguments ...interface{}) on Mock to remove all "matched expectations" and then Unset on Call to remove just that call expectation.
At minimum, the docs should be updated to reflect actual behavior (see first item in additional information below).
Walk Expected Calls and manually look for the match - While technically possible, there are private methods in testify that have all this logic already (a new findExactMatch is likely needed) and re-creating this is not trivial.
Walk Expected Calls and remove all calls for a method - This is rather straightforward to do but has two downsides: 1) It requires all expectations for that method to be setup again; 2) It's not chainable
Additional Information
Why this should be considered a bug vs expected behavior - The docs indicate "removes a mock handler from being called." (emphasis mine). However, it will remove 1 to N handlers where N is any handler that "satisfies" the arguments in the call since it removes all of them (vs. "a" handler). As mentioned above, the API requires calling On first (unless the call is tracked) which adds a new call then unsets it (and any others that satisfy) vs. just having a Unset on Mock that will remove any that match, leading to the 1 to N vs. 0 to 1 behavior. Beyond just not matching the docs, regarding why this is relevant, question / feature-suggestion: built-in way to overwrite / clear ExpectedCalls? #558 covers this well but in short, in a complicated Mock, it is common to setup default expectations and then within an individual test, change those expectations for just that test. For example, if an interface has 10 methods all of which a set of tests use but each test is testing individual portions, a NewMyServiceMock utility method (aka. builder) could establish defaults which the test than can override/reset/clear. In this case, each test gets a new Mock but the mock is created by NewMyServiceMock with defaults. Depending on what the defaults are, there is currently no way to change expectations of specific arguments (vs. any expectation that satisfies the arguments).
The one benefit of the current code is that it allows for "clearing all" expectations for a given method (e.g. On("TheExampleMethod1", Anything, Anything).Unset() would remove all expectations from TheExampleMethod1 that had two parameters) as there isn't an otherwise available API for this currently. This is likely an unintended benefit however, and possibly creating an UnsetAll(methodName string) would continue to support this for anyone taking advantage of it.
Reviewing the code, I believe this is occurring because Unsetrelies on logic similar to MethodCalled and therefore detects any remaining available expectation that satisfies the arguments vs. the exact arguments.
I'm happy to work on a PR for this however prior to doing so, want to solicit feedback on whether or not there is agreement on the issue described being a bug (vs. expected behavior) and assuming yes, it is a bug, confirming the expected behavior. My proposed solution would be as follows:
Unset should do an exact match on args
All exact matches would be removed from expectations
if no matches found, it's a no-op - Technically, due to the way unset currently works, the call already has to exist at least once since On("TheExampleMethod1", Anything, Anything).Unset() actually adds the expectation and then removes it (along with any others that match).
Alternative Solution
If its determined that this is not a bug or even if it is a bug but its determined that changing behavior would not be desirable, alternate approaches could be:
Option 1
Add UnsetOnly (or similar) to Call to remove only the call its invoked against
Add UnsetAll() to Mock to remove all expectations
Add UnsetMethod(methodName string) to Mock to remove all expectations for a method
Add UnsetCall(string methodName, methodName string, arguments ...interface{}) to Mock which would behave identical to what Call.Unset() does currently but avoids having to call On only to then call Unset
Add UnsetExactCall(string methodName, methodName string, arguments ...interface{}) to Mock to remove all calls that have exact matched arguments
** Could combine 3 & 4 to UnsetCall(exact bool, string methodName, methodName string, arguments ...interface{})
Option 2
Expose, either directly or indirectly, findClosestCall, findExpectedCall and findExactCalls (which would be new and return 0 to N calls) to provide package consumers control over manually manipulating Mock.ExpectedCalls. This is likely less than ideal but at least would give consumers of package an option to accomplish the desired behavior
Related Issues
#1511 proposes a Replace API which is often why Unset is needed. If #1511 were merged currently, it would replace all "matched" expectations which wouldn't necessarily be only one expectation
Description
When calling
Unset, any expected call that satisfies the arguments in the call being unset is removed fromExpectedCalls.For example, in the following scenarios, both the
Anything, Anythingand3,3calls are removed:Reproduction
Go Playground is timing out on build when
github.com/stretchr/testify/mockis used (likely due to dependencies downloading), so submitted #1622 with tests to reproduce.Step To Reproduce
Run any/all of the following tests:
Expected behavior
Only the call(s) with the exact matched arguments should be unset/removed.
Ideally only the call that
Unsetis invoked against is removed but the current API is written in a manner that requires aCallto unset so unless the call is tracked (e.g.,c := mockService.On(....)),Onmust be called first thenUnsetwhich adds the expectation only to immediately remove it along with any other "matched" expectations. #982 which addedUnsetactually started withUnsetonMockbut then moved it toCallto supportUnseton aCallbut possibly it could have considered leavingUnset(methodName string, arguments ...interface{})on Mock to remove all "matched expectations" and thenUnsetonCallto remove just that call expectation.At minimum, the docs should be updated to reflect actual behavior (see first item in additional information below).
Actual behavior
All calls that satisfy the arguments are removed
Current Workarounds
ExpectedCalls- This guidance has been given in several issues (including question / feature-suggestion: built-in way to overwrite / clear ExpectedCalls? #558 which spawnedUnsetbeing introduced) but has the downside of clearing all expected calls, not just the desired calltestifythat have all this logic already (a newfindExactMatchis likely needed) and re-creating this is not trivial.Additional Information
Onfirst (unless the call is tracked) which adds a new call then unsets it (and any others that satisfy) vs. just having aUnsetonMockthat will remove any that match, leading to the 1 to N vs. 0 to 1 behavior. Beyond just not matching the docs, regarding why this is relevant, question / feature-suggestion: built-in way to overwrite / clear ExpectedCalls? #558 covers this well but in short, in a complicated Mock, it is common to setup default expectations and then within an individual test, change those expectations for just that test. For example, if an interface has 10 methods all of which a set of tests use but each test is testing individual portions, aNewMyServiceMockutility method (aka. builder) could establish defaults which the test than can override/reset/clear. In this case, each test gets a new Mock but the mock is created byNewMyServiceMockwith defaults. Depending on what the defaults are, there is currently no way to change expectations of specific arguments (vs. any expectation that satisfies the arguments).On("TheExampleMethod1", Anything, Anything).Unset()would remove all expectations fromTheExampleMethod1that had two parameters) as there isn't an otherwise available API for this currently. This is likely an unintended benefit however, and possibly creating anUnsetAll(methodName string)would continue to support this for anyone taking advantage of it.Unsetrelies on logic similar to MethodCalled and therefore detects any remaining available expectation that satisfies the arguments vs. the exact arguments.On("TheExampleMethod1", Anything, Anything).Unset()actually adds the expectation and then removes it (along with any others that match).Alternative Solution
If its determined that this is not a bug or even if it is a bug but its determined that changing behavior would not be desirable, alternate approaches could be:
Option 1
UnsetOnly(or similar) toCallto remove only the call its invoked againstUnsetAll()toMockto remove all expectationsUnsetMethod(methodName string)toMockto remove all expectations for a methodUnsetCall(string methodName, methodName string, arguments ...interface{})toMockwhich would behave identical to whatCall.Unset()does currently but avoids having to callOnonly to then callUnsetUnsetExactCall(string methodName, methodName string, arguments ...interface{})toMockto remove all calls that have exact matched arguments** Could combine 3 & 4 to
UnsetCall(exact bool, string methodName, methodName string, arguments ...interface{})Option 2
findExactCalls(which would be new and return 0 to N calls) to provide package consumers control over manually manipulatingMock.ExpectedCalls. This is likely less than ideal but at least would give consumers of package an option to accomplish the desired behaviorRelated Issues
#1511 proposes a
ReplaceAPI which is often whyUnsetis needed. If #1511 were merged currently, it would replace all "matched" expectations which wouldn't necessarily be only one expectation