1010from decoy .spy import AsyncSpy , Spy
1111from 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
2516def 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
34129def 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-
54143def 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
66157def 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
78171def 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
92187def 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
107202def 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
115210def 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
123218def 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
138233def 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
206242def 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" )
245251def 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