Skip to content

Commit f5fb042

Browse files
committed
Guard sameish invocation matching against matcher predicate errors
The v2 continuation dedup path compares stubbed invocations in both matching directions to detect "sameish" signatures. This can evaluate arg_that predicates against internal matcher objects (for example Any), which may raise TypeError/ValueError in user predicates. Treat those exceptions as "not sameish" during continuation detection instead of failing stubbing. This keeps arg_that behavior unchanged for normal runtime matching while making internal dedup robust. Add a regression test in tests/chaining_test.py that reproduces the failure with arg_that(lambda value: value > 0) next to any(). Ref #119
1 parent 182b916 commit f5fb042

2 files changed

Lines changed: 34 additions & 11 deletions

File tree

mockito/mocking.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -399,16 +399,28 @@ def set_continuation(self, continuation: invocation.ConfiguredContinuation) -> N
399399
def _sameish_invocations(
400400
self, same: invocation.StubbedInvocation
401401
) -> list[invocation.StubbedInvocation]:
402-
return [
403-
invoc
404-
for invoc in self.stubbed_invocations
405-
if (
406-
invoc is not same
407-
and invoc.method_name == same.method_name
408-
and invoc.matches(same)
409-
and same.matches(invoc)
410-
)
411-
]
402+
sameish: list[invocation.StubbedInvocation] = []
403+
for invoc in self.stubbed_invocations:
404+
if invoc is same:
405+
continue
406+
407+
if invoc.method_name != same.method_name:
408+
continue
409+
410+
if self._invocations_are_sameish(invoc, same):
411+
sameish.append(invoc)
412+
413+
return sameish
414+
415+
def _invocations_are_sameish(
416+
self,
417+
left: invocation.StubbedInvocation,
418+
right: invocation.StubbedInvocation,
419+
) -> bool:
420+
try:
421+
return left.matches(right) and right.matches(left)
422+
except Exception:
423+
return False
412424

413425
def get_original_method(self, method_name: str) -> object | None:
414426
return self._original_methods.get(method_name, None)

tests/chaining_test.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22

3-
from mockito import expect, mock, verify, unstub, when
3+
from mockito import any as any_
4+
from mockito import arg_that, expect, mock, verify, unstub, when
45
from mockito.invocation import AnswerError, InvocationError
56

67

@@ -469,6 +470,16 @@ def test_chain_matching_requires_candidate_matches_existing_direction():
469470
assert cat.meow(2).purr() == "two"
470471

471472

473+
def test_sameish_matching_does_not_evaluate_arg_that_predicate_on_matchers():
474+
cat = mock()
475+
476+
when(cat).meow(any_()).thenReturn("any")
477+
when(cat).meow(arg_that(lambda value: value > 0)).thenReturn("positive")
478+
479+
assert cat.meow(1) == "positive"
480+
assert cat.meow(-1) == "any"
481+
482+
472483
def test_unexpected_chain_segment_arguments_raise_invocation_error_early():
473484
cat = mock()
474485

0 commit comments

Comments
 (0)