Skip to content

Commit 45b5850

Browse files
committed
test: refactor test_mock.py to align with v3
1 parent 446d7dc commit 45b5850

File tree

1 file changed

+152
-130
lines changed

1 file changed

+152
-130
lines changed

tests/test_mock.py

Lines changed: 152 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -10,78 +10,173 @@
1010
from decoy.spy import AsyncSpy, Spy
1111
from decoy.warnings import IncorrectCallWarning
1212

13-
from .fixtures import (
14-
SomeAsyncCallableClass,
15-
SomeAsyncClass,
16-
SomeCallableClass,
17-
SomeClass,
18-
SomeNestedClass,
19-
noop,
20-
some_async_func,
21-
some_func,
22-
)
13+
from . import fixtures
2314

2415

2516
def test_create_mock(decoy: Decoy) -> None:
17+
"""It creates a callable mock that no-ops."""
18+
subject = decoy.mock(name="alice")
19+
result = subject()
20+
21+
assert result is None
22+
assert isinstance(subject, Spy)
23+
assert repr(subject) == "<Decoy mock `alice`>"
24+
assert inspect.signature(subject) == inspect.signature(fixtures.noop)
25+
26+
27+
def test_create_mock_requires_name(decoy: Decoy) -> None:
28+
"""It requires a name for a mock with no spec."""
29+
with pytest.raises(errors.MockNameRequiredError):
30+
decoy.mock() # type:ignore[call-overload]
31+
32+
33+
def test_child_mock(decoy: Decoy) -> None:
34+
"""The attributes of a mock are also mocks."""
35+
parent = decoy.mock(name="alice")
36+
subject = parent.child
37+
result = subject()
38+
39+
assert subject is parent.child
40+
assert result is None
41+
assert isinstance(subject, Spy)
42+
assert repr(subject) == "<Decoy mock `alice.child`>"
43+
assert inspect.signature(subject) == inspect.signature(fixtures.noop)
44+
45+
46+
def test_attribute_set_and_delete(decoy: Decoy) -> None:
47+
"""The caller may set attributes on a mock and delete them to reset them."""
48+
subject = decoy.mock(name="subject")
49+
50+
subject.prop_name = 41
51+
assert subject.prop_name == 41
52+
53+
del subject.prop_name
54+
assert isinstance(subject.prop_name, Spy)
55+
56+
57+
async def test_create_async_mock(decoy: Decoy) -> None:
58+
"""It creates an async callable mock that no-ops."""
59+
subject = decoy.mock(name="alice", is_async=True)
60+
result = await subject()
61+
62+
assert result is None
63+
assert isinstance(subject, AsyncSpy)
64+
assert repr(subject) == "<Decoy mock `alice`>"
65+
assert inspect.signature(subject) == inspect.signature(fixtures.noop)
66+
67+
68+
def test_create_func_mock(decoy: Decoy) -> None:
69+
"""It creates a mock based on a function."""
70+
subject = decoy.mock(func=fixtures.some_func)
71+
result = subject("hello")
72+
73+
assert result is None
74+
assert isinstance(subject, Spy)
75+
assert repr(subject) == "<Decoy mock `tests.fixtures.some_func`>"
76+
assert inspect.signature(subject) == inspect.signature(fixtures.some_func)
77+
78+
79+
async def test_create_async_func_mock(decoy: Decoy) -> None:
80+
"""It creates a mock based on an async function."""
81+
subject = decoy.mock(func=fixtures.some_async_func)
82+
result = await subject("hello")
83+
84+
assert result is None
85+
assert isinstance(subject, AsyncSpy)
86+
assert repr(subject) == "<Decoy mock `tests.fixtures.some_async_func`>"
87+
assert inspect.signature(subject) == inspect.signature(fixtures.some_async_func)
88+
89+
90+
def test_create_callable_class_mock(decoy: Decoy) -> None:
91+
"""It creates a mock based on a callable class."""
92+
subject = decoy.mock(cls=fixtures.SomeCallableClass)
93+
result = subject(123)
94+
95+
def _expected_signature(val: int) -> int:
96+
raise NotImplementedError()
97+
98+
assert result is None
99+
assert isinstance(subject, fixtures.SomeCallableClass)
100+
assert isinstance(subject, Spy)
101+
assert repr(subject) == "<Decoy mock `tests.fixtures.SomeCallableClass`>"
102+
assert inspect.signature(subject) == inspect.signature(_expected_signature)
103+
104+
105+
async def test_create_async_callable_class_mock(decoy: Decoy) -> None:
106+
"""It creates a mock based on an async callable class."""
107+
subject = decoy.mock(cls=fixtures.SomeAsyncCallableClass)
108+
result = await subject(123)
109+
110+
def _expected_signature(val: int) -> int:
111+
raise NotImplementedError()
112+
113+
assert result is None
114+
assert isinstance(subject, fixtures.SomeAsyncCallableClass)
115+
assert isinstance(subject, AsyncSpy)
116+
assert repr(subject) == "<Decoy mock `tests.fixtures.SomeAsyncCallableClass`>"
117+
assert inspect.signature(subject) == inspect.signature(_expected_signature)
118+
119+
120+
def test_create_class_mock(decoy: Decoy) -> None:
26121
"""It creates a mock from a class spec."""
27-
subject = decoy.mock(cls=SomeClass)
122+
subject = decoy.mock(cls=fixtures.SomeClass)
28123

29-
assert isinstance(subject, SomeClass)
124+
assert isinstance(subject, fixtures.SomeClass)
30125
assert isinstance(subject, Spy)
31126
assert repr(subject) == "<Decoy mock `tests.fixtures.SomeClass`>"
32127

33128

34129
def test_create_method_mock(decoy: Decoy) -> None:
35130
"""It creates a child mock from a class spec."""
36-
subject = decoy.mock(cls=SomeClass).foo
131+
subject = decoy.mock(cls=fixtures.SomeClass).foo
132+
result = subject("hello")
37133

38134
def _expected_signature(val: str) -> str:
39135
raise NotImplementedError()
40136

137+
assert result is None
41138
assert isinstance(subject, Spy)
42139
assert inspect.signature(subject) == inspect.signature(_expected_signature)
43140
assert repr(subject) == "<Decoy mock `tests.fixtures.SomeClass.foo`>"
44141

45142

46-
def test_method_noop(decoy: Decoy) -> None:
47-
"""A method mock no-ops by default."""
48-
subject = decoy.mock(cls=SomeClass)
49-
result = subject.foo("hello")
50-
51-
assert result is None
52-
53-
54143
def test_create_staticmethod_mock(decoy: Decoy) -> None:
55144
"""It creates a child staticmethod mock from a class spec."""
56-
subject = decoy.mock(cls=SomeClass).static_method
145+
subject = decoy.mock(cls=fixtures.SomeClass).static_method
146+
result = subject("hello")
57147

58148
def _expected_signature(hello: str) -> int:
59149
raise NotImplementedError()
60150

151+
assert result is None
61152
assert isinstance(subject, Spy)
62153
assert inspect.signature(subject) == inspect.signature(_expected_signature)
63154
assert repr(subject) == "<Decoy mock `tests.fixtures.SomeClass.static_method`>"
64155

65156

66157
def test_create_classmethod_mock(decoy: Decoy) -> None:
67158
"""It creates a child classmethod mock from a class spec."""
68-
subject = decoy.mock(cls=SomeClass).class_method
159+
subject = decoy.mock(cls=fixtures.SomeClass).class_method
160+
result = subject("hello")
69161

70162
def _expected_signature(hello: str) -> int:
71163
raise NotImplementedError()
72164

165+
assert result is None
73166
assert isinstance(subject, Spy)
74167
assert inspect.signature(subject) == inspect.signature(_expected_signature)
75168
assert repr(subject) == "<Decoy mock `tests.fixtures.SomeClass.class_method`>"
76169

77170

78171
def test_create_decorated_method_mock(decoy: Decoy) -> None:
79172
"""It creates a child mock of a decorated method from a class spec."""
80-
subject = decoy.mock(cls=SomeClass).some_wrapped_method
173+
subject = decoy.mock(cls=fixtures.SomeClass).some_wrapped_method
174+
result = subject("hello")
81175

82176
def _expected_signature(val: str) -> str:
83177
raise NotImplementedError()
84178

179+
assert result is None
85180
assert isinstance(subject, Spy)
86181
assert inspect.signature(subject) == inspect.signature(_expected_signature)
87182
assert (
@@ -90,13 +185,13 @@ def _expected_signature(val: str) -> str:
90185

91186

92187
def test_create_attribute_mock(decoy: Decoy) -> None:
93-
"""It creates a child mock of an attribute."""
94-
subject = decoy.mock(cls=SomeClass).some_attr
188+
"""It creates a child mock of a class's primitive attribute."""
189+
subject = decoy.mock(cls=fixtures.SomeClass).some_attr
95190

96191
expected_signature = (
97192
inspect.signature(bool)
98193
if sys.version_info >= (3, 13)
99-
else inspect.signature(noop)
194+
else inspect.signature(fixtures.noop)
100195
)
101196

102197
assert isinstance(subject, Spy)
@@ -106,141 +201,52 @@ def test_create_attribute_mock(decoy: Decoy) -> None:
106201

107202
def test_create_attribute_class_mock(decoy: Decoy) -> None:
108203
"""It creates a child class mock from an attribute."""
109-
subject = decoy.mock(cls=SomeNestedClass).child_attr
204+
subject = decoy.mock(cls=fixtures.SomeNestedClass).child_attr
110205

111206
assert isinstance(subject, Spy)
112-
assert isinstance(subject, SomeClass)
207+
assert isinstance(subject, fixtures.SomeClass)
113208

114209

115210
def test_create_property_class_mock(decoy: Decoy) -> None:
116211
"""It creates a child class mock from an property getter."""
117-
subject = decoy.mock(cls=SomeNestedClass).child
212+
subject = decoy.mock(cls=fixtures.SomeNestedClass).child
118213

119214
assert isinstance(subject, Spy)
120-
assert isinstance(subject, SomeClass)
215+
assert isinstance(subject, fixtures.SomeClass)
121216

122217

123218
def test_create_optional_property_class_mock(decoy: Decoy) -> None:
124219
"""It creates a child class mock from an property getter with Optional return."""
125-
subject = decoy.mock(cls=SomeNestedClass).optional_child
220+
subject = decoy.mock(cls=fixtures.SomeNestedClass).optional_child
126221
assert isinstance(subject, Spy)
127-
assert isinstance(subject, SomeClass)
222+
assert isinstance(subject, fixtures.SomeClass)
128223

129-
subject = decoy.mock(cls=SomeNestedClass).union_child_and_none
224+
subject = decoy.mock(cls=fixtures.SomeNestedClass).union_child_and_none
130225
assert isinstance(subject, Spy)
131-
assert isinstance(subject, SomeClass)
226+
assert isinstance(subject, fixtures.SomeClass)
132227

133-
subject = decoy.mock(cls=SomeNestedClass).union_none_and_child
228+
subject = decoy.mock(cls=fixtures.SomeNestedClass).union_none_and_child
134229
assert isinstance(subject, Spy)
135-
assert isinstance(subject, SomeClass)
230+
assert isinstance(subject, fixtures.SomeClass)
136231

137232

138233
def test_create_union_class_mock(decoy: Decoy) -> None:
139234
"""A child class mock from an property with union return is not typed."""
140-
subject = decoy.mock(cls=SomeNestedClass).union_child
141-
142-
assert isinstance(subject, Spy)
143-
assert not isinstance(subject, SomeClass)
144-
assert not isinstance(subject, SomeAsyncClass)
145-
146-
147-
def test_create_callable_mock(decoy: Decoy) -> None:
148-
"""It creates a mock from a callable class."""
149-
subject = decoy.mock(cls=SomeCallableClass)
150-
151-
def _expected_signature(val: int) -> int:
152-
raise NotImplementedError()
153-
154-
assert isinstance(subject, Spy)
155-
assert isinstance(subject, SomeCallableClass)
156-
assert inspect.signature(subject) == inspect.signature(_expected_signature)
157-
158-
159-
def test_create_async_callable_mock(decoy: Decoy) -> None:
160-
"""It creates a mock from an async callable class."""
161-
subject = decoy.mock(cls=SomeAsyncCallableClass)
162-
163-
async def _expected_signature(val: int) -> int:
164-
raise NotImplementedError()
165-
166-
assert isinstance(subject, AsyncSpy)
167-
assert isinstance(subject, SomeAsyncCallableClass)
168-
assert inspect.signature(subject) == inspect.signature(_expected_signature)
169-
assert inspect.iscoroutinefunction(subject.__call__)
170-
171-
172-
def test_create_func_mock(decoy: Decoy) -> None:
173-
"""It creates a mock from a function spec."""
174-
subject = decoy.mock(func=some_func)
235+
subject = decoy.mock(cls=fixtures.SomeNestedClass).union_child
175236

176237
assert isinstance(subject, Spy)
177-
assert inspect.signature(subject) == inspect.signature(some_func)
178-
assert repr(subject) == "<Decoy mock `tests.fixtures.some_func`>"
179-
180-
181-
def test_func_noop(decoy: Decoy) -> None:
182-
"""A function mock no-ops by default."""
183-
subject = decoy.mock(func=some_func)
184-
result = subject("hello")
185-
186-
assert result is None
187-
188-
189-
def test_create_async_func_mock(decoy: Decoy) -> None:
190-
"""It creates a mock from an async function spec."""
191-
subject = decoy.mock(func=some_async_func)
192-
193-
assert isinstance(subject, AsyncSpy)
194-
assert inspect.signature(subject) == inspect.signature(some_async_func)
195-
assert repr(subject) == "<Decoy mock `tests.fixtures.some_async_func`>"
196-
197-
198-
async def test_async_func_noop(decoy: Decoy) -> None:
199-
"""An async function mock no-ops by default."""
200-
subject = decoy.mock(func=some_async_func)
201-
result = await subject("hello")
202-
203-
assert result is None
238+
assert not isinstance(subject, fixtures.SomeClass)
239+
assert not isinstance(subject, fixtures.SomeAsyncClass)
204240

205241

206242
def test_func_bad_call(decoy: Decoy) -> None:
207243
"""It raises an IncorrectCallWarning if call is bad."""
208-
subject = decoy.mock(func=some_func)
244+
subject = decoy.mock(func=fixtures.some_func)
209245

210246
with pytest.warns(IncorrectCallWarning):
211247
subject("hello", "world") # type: ignore[call-arg]
212248

213249

214-
def test_create_specless_mock(decoy: Decoy) -> None:
215-
"""It creates a mock without a spec."""
216-
subject = decoy.mock(name="subject")
217-
218-
assert isinstance(subject, Spy)
219-
assert repr(subject) == "<Decoy mock `subject`>"
220-
221-
222-
def test_create_specless_async_mock(decoy: Decoy) -> None:
223-
"""It creates an async mock without a spec."""
224-
subject = decoy.mock(name="subject", is_async=True)
225-
226-
assert isinstance(subject, AsyncSpy)
227-
assert inspect.iscoroutinefunction(subject.__call__)
228-
229-
230-
async def test_async_specless_mock_noop(decoy: Decoy) -> None:
231-
"""An async specless mock no-ops by default."""
232-
subject = decoy.mock(name="subject", is_async=True)
233-
result = await subject("hello")
234-
235-
assert result is None
236-
237-
238-
def test_mock_name_required(decoy: Decoy) -> None:
239-
"""A name is required for a mock without a spec."""
240-
with pytest.raises(errors.MockNameRequiredError):
241-
decoy.mock() # type: ignore[call-overload]
242-
243-
244250
@pytest.mark.filterwarnings("ignore:'NoneType' object is not subscriptable")
245251
def test_bad_type_hints(decoy: Decoy) -> None:
246252
"""It tolerates bad type hints without failing at runtime."""
@@ -251,3 +257,19 @@ class _BadTypeHints:
251257
subject = decoy.mock(cls=_BadTypeHints).not_ok
252258

253259
assert isinstance(subject, Spy)
260+
261+
262+
def test_context_manager(decoy: Decoy) -> None:
263+
"""It creates a context manager."""
264+
subject = decoy.mock(name="cm")
265+
266+
with subject as result:
267+
assert result is None
268+
269+
270+
async def test_async_context_manager(decoy: Decoy) -> None:
271+
"""It creates an async context manager."""
272+
subject = decoy.mock(name="acm")
273+
274+
async with subject as result:
275+
assert result is None

0 commit comments

Comments
 (0)