Skip to content

Commit 3d0ffe6

Browse files
committed
Make captor-based roots sameish for chain continuation deduping
Continuation lookup now treats captor-style wildcard roots as structurally sameish so equivalent root selectors share the same chain continuation. Specifically, sameish comparison now handles call_captor(), *captor(), and **captor() sentinel wrappers without relying on object identity, and compares sentinel wrappers via their underlying captor matcher semantics. The tests were updated to reflect this contract in sameish unit coverage, and chaining coverage now includes call_captor/*captor/**captor branch-sharing scenarios.
1 parent 3abfc78 commit 3d0ffe6

File tree

3 files changed

+112
-10
lines changed

3 files changed

+112
-10
lines changed

mockito/sameish.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,32 @@ def _values_are_sameish(left: object, right: object) -> bool:
5454
if left is Ellipsis or right is Ellipsis:
5555
return left is right
5656

57+
if matchers.is_call_captor(left) and matchers.is_call_captor(right):
58+
return True
59+
60+
if matchers.is_call_captor(left) or matchers.is_call_captor(right):
61+
return False
62+
63+
if (
64+
matchers.is_captor_args_sentinel(left)
65+
and matchers.is_captor_args_sentinel(right)
66+
):
67+
return _values_are_sameish(left.captor.matcher, right.captor.matcher)
68+
69+
if (
70+
matchers.is_captor_kwargs_sentinel(left)
71+
and matchers.is_captor_kwargs_sentinel(right)
72+
):
73+
return _values_are_sameish(left.captor.matcher, right.captor.matcher)
74+
75+
if (
76+
matchers.is_captor_args_sentinel(left)
77+
or matchers.is_captor_args_sentinel(right)
78+
or matchers.is_captor_kwargs_sentinel(left)
79+
or matchers.is_captor_kwargs_sentinel(right)
80+
):
81+
return False
82+
5783
if isinstance(left, matchers.Matcher) and isinstance(right, matchers.Matcher):
5884
return _matchers_are_sameish(left, right)
5985

@@ -112,12 +138,6 @@ def _matchers_are_sameish( # noqa: C901
112138
):
113139
return _values_are_sameish(left.matcher, right.matcher)
114140

115-
if (
116-
isinstance(left, matchers.CallCaptor)
117-
and isinstance(right, matchers.CallCaptor)
118-
):
119-
return False
120-
121141
return _equals_or_identity(left, right)
122142

123143

tests/chaining_test.py

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

33
from mockito import any as any_
4-
from mockito import arg_that, expect, mock, verify, unstub, when
4+
from mockito import arg_that, call_captor, captor, expect, mock, verify, unstub, when
55
from mockito.invocation import AnswerError, InvocationError
66

77

@@ -60,6 +60,39 @@ def test_multiple_chain_branches_with_same_arg_that_matcher_share_root():
6060
assert cat_that_meowed.roll() == "playful"
6161

6262

63+
def test_multiple_chain_branches_with_call_captor_roots_share_root():
64+
cat = mock()
65+
66+
when(cat).meow(call_captor()).purr().thenReturn("friendly")
67+
when(cat).meow(call_captor()).roll().thenReturn("playful")
68+
69+
cat_that_meowed = cat.meow(1)
70+
assert cat_that_meowed.purr() == "friendly"
71+
assert cat_that_meowed.roll() == "playful"
72+
73+
74+
def test_multiple_chain_branches_with_args_captor_roots_share_root():
75+
cat = mock()
76+
77+
when(cat).meow(*captor()).purr().thenReturn("friendly")
78+
when(cat).meow(*captor()).roll().thenReturn("playful")
79+
80+
cat_that_meowed = cat.meow(1, 2)
81+
assert cat_that_meowed.purr() == "friendly"
82+
assert cat_that_meowed.roll() == "playful"
83+
84+
85+
def test_multiple_chain_branches_with_kwargs_captor_roots_share_root():
86+
cat = mock()
87+
88+
when(cat).meow(**captor()).purr().thenReturn("friendly")
89+
when(cat).meow(**captor()).roll().thenReturn("playful")
90+
91+
cat_that_meowed = cat.meow(volume=1)
92+
assert cat_that_meowed.purr() == "friendly"
93+
assert cat_that_meowed.roll() == "playful"
94+
95+
6396
def test_unstub_child_chain_then_reconfigure_does_not_leave_stale_root_stub():
6497
cat = mock()
6598

tests/sameish_test.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from dataclasses import dataclass, field
22

3-
from mockito import and_, any as any_, arg_that, call_captor, eq, gt, neq, or_
3+
from mockito import and_, any as any_, arg_that, call_captor, captor, eq, gt, neq, or_
44
from mockito import sameish
55

66

@@ -108,20 +108,69 @@ def predicate(value):
108108
assert seen == []
109109

110110

111-
def test_call_captor_instances_are_not_interchangeable():
111+
def test_call_captor_instances_are_sameish_for_root_deduping():
112112
left = call_captor()
113113
right = call_captor()
114114

115115
assert sameish.invocations_are_sameish(
116116
bar(left),
117117
bar(left),
118118
)
119-
assert not sameish.invocations_are_sameish(
119+
assert sameish.invocations_are_sameish(
120+
bar(left),
121+
bar(right),
122+
)
123+
124+
125+
def test_argument_captor_instances_are_sameish_for_root_deduping():
126+
left = captor()
127+
right = captor()
128+
129+
assert sameish.invocations_are_sameish(
130+
bar(left),
131+
bar(left),
132+
)
133+
assert sameish.invocations_are_sameish(
120134
bar(left),
121135
bar(right),
122136
)
123137

124138

139+
def test_star_argument_captor_instances_are_sameish_for_root_deduping():
140+
left = captor()
141+
right = captor()
142+
143+
assert sameish.invocations_are_sameish(
144+
bar(1, *left),
145+
bar(1, *left),
146+
)
147+
assert sameish.invocations_are_sameish(
148+
bar(1, *left),
149+
bar(1, *right),
150+
)
151+
152+
153+
def test_kwargs_argument_captor_instances_are_sameish_for_root_deduping():
154+
left = captor()
155+
right = captor()
156+
157+
assert sameish.invocations_are_sameish(
158+
bar(1, **left),
159+
bar(1, **left),
160+
)
161+
assert sameish.invocations_are_sameish(
162+
bar(1, **left),
163+
bar(1, **right),
164+
)
165+
166+
167+
def test_argument_captor_instances_with_different_matchers_are_not_sameish():
168+
assert not sameish.invocations_are_sameish(
169+
bar(captor(any_(int))),
170+
bar(captor(any_(str))),
171+
)
172+
173+
125174
def test_eq_failures_fallback_to_identity():
126175
class EqBoom:
127176
def __eq__(self, other):

0 commit comments

Comments
 (0)