-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcase.py
343 lines (273 loc) · 11 KB
/
case.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
from unittest import TestCase as BaseTestCase
from typing import Any, Dict
from abc import abstractmethod
from warnings import warn
from copy import deepcopy
from unittest_extensions.error import TestError
class TestCase(BaseTestCase):
"""
Extends unittest.TestCase with methods that assert the result of a defined
`subject` method.
```
from unittest_extensions import TestCase, args
class MyClass:
def my_method(self, a, b):
return a + b
class TestMyMethod(TestCase):
def subject(self, a, b):
return MyClass().my_method(a, b)
@args({"a": None, "b": 2})
def test_none_plus_int(self):
self.assertResultRaises(TypeError)
@args({"a": 10, "b": 22.1})
def test_int_plus_float(self):
self.assertResult(32.1)
```
"""
@abstractmethod
def subject(self, **kwargs) -> Any: ...
def subjectKwargs(self) -> Dict[str, Any]:
"""
Return the keyword arguments of the subject.
The dictionary returned is a copy of the original arguments. Thus,
the arguments that the subject receives cannot be mutated by mutating
the returned object of this method.
"""
# NOTE: deepcopy keeps a reference of the copied object. This can cause
# issues with memory.
return deepcopy(self._subjectKwargs)
def result(self) -> Any:
"""
Result of the `subject` called with arguments defined by the `args`
decorator.
"""
try:
self._subjectResult = self.subject(**self._subjectKwargs)
return self._subjectResult
except Exception as e:
if len(e.args) == 0:
raise e
msg = str(e.args[0])
if "subject() got an unexpected keyword argument" in msg:
raise TestError(
"Subject received "
+ msg.split("subject() got ")[1]
+ ". Did you decorate a test method with the wrong 'args'?"
)
elif "subject() missing" in msg and ("required positional argument" in msg):
raise TestError(
"Subject misses "
+ msg.split("subject() missing ")[1]
+ ". Did you decorate all test methods with 'args'?"
)
raise e
def cachedResult(self) -> Any:
"""
Return the result of the last `subject` call.
Use this function when you want to assert different attributes of your
subject without executing it multiple times.
Raises `unittest_extensions.TestError` if subject has not been called.
The returned object is a copy of the result. Thus, the result cannot be
mutated by mutating the returned object of this method.
"""
if not hasattr(self, "_subjectResult"):
raise TestError("Cannot call 'cachedResult' before calling 'result'")
# NOTE: deepcopy keeps a reference of the copied object. This can cause
# issues with memory.
return deepcopy(self._subjectResult)
def assertResult(self, value):
"""
Fail if the result is unequal to the value as determined by the '=='
operator.
Equivalent to `assertEqual(self.result(), value)`.
"""
self.assertEqual(self.result(), value)
def assertResultNot(self, value):
"""
Fail if the result is equal to the value as determined by the '=='
operator.
Equivalent to `assertNotEqual(self.result(), value)`.
"""
self.assertNotEqual(self.result(), value)
def assertResultTrue(self):
"""
Check that the result is true.
Equivalent to `self.assertTrue(self.result())`.
"""
self.assertTrue(self.result())
def assertResultFalse(self):
"""
Check that the result is false.
Equivalent to `assertFalse(self.result())`.
"""
self.assertFalse(self.result())
def assertResultIs(self, value):
"""
Just like self.assertTrue(self.result() is value), but with a
nicer default message.
Equivalent to `assertIs(self.result(), value)`.
"""
self.assertIs(self.result(), value)
def assertResultIsNot(self, value):
"""
Just like self.assertTrue(self.result() is not value), but with a
nicer default message.
Equivalent to `assertIsNot(self.result(), value)`.
"""
self.assertIsNot(self.result(), value)
def assertResultIn(self, container):
"""
Just like self.assertTrue(self.result() in container), but with a
nicer default message.
Equivalent to `assertIn(self.result(), container)`.
"""
self.assertIn(self.result(), container)
def assertResultNotIn(self, container):
"""
Just like self.assertTrue(self.result() not in container), but
with a nicer default message.
Equivalent to `assertNotIn(self.result(), container)`.
"""
self.assertNotIn(self.result(), container)
def assertResultIsInstance(self, cls):
"""
Just like self.assertTrue(self.result() in container), but with a
nicer default message.
Equivalent to `assertIsInstance(self.result(), cls)`.
"""
self.assertIsInstance(self.result(), cls)
def assertResultIsNotInstance(self, cls):
"""
Just like self.assertTrue(self.result() not in container), but with a
nicer default message.
Equivalent to `assertNotIsInstance(self.result(), cls)`.
"""
self.assertNotIsInstance(self.result(), cls)
def assertResultRaises(self, expected_exception):
"""
Fail unless an exception of class expected_exception is raised by the
result. If a different type of exception is raised, it will not be
caught, and the test case will be deemed to have suffered an error,
exactly as for an unexpected exception.
Equivalent to
```
with self.assertRaises(expected_exception):
self.result()
```
"""
with self.assertRaises(expected_exception):
self.result()
def assertResultRaisesRegex(self, expected_exception, expected_regex):
"""
Fail unless an exception of class expected_exception is raised by the
result and the message matches the regex.
Equivalent to
```
with self.assertRaisesRegex(expected_exception, expected_regex):
self.result()
```
"""
with self.assertRaisesRegex(expected_exception, expected_regex):
self.result()
def assertResultAlmost(self, value, places=None, delta=None):
"""
Fail if the result is unequal to the value as determined by their
difference rounded to the given number of decimal places (default 7)
and comparing to zero, or by comparing that the difference between the
two objects is more than the given delta.
Equivalent to `assertAlmostEqual(self.result(), value, places, delta=delta).`
"""
self.assertAlmostEqual(self.result(), value, places, delta=delta)
def assertResultNotAlmost(self, value, places=None, delta=None):
"""
Fail if the result is equal to the value as determined by their
difference rounded to the given number of decimal places (default 7)
and comparing to zero, or by comparing that the difference between the
two objects is less than the given delta.
Equivalent to `assertResultNotAlmost(self, value, places=None, delta=None)`.
"""
self.assertNotAlmostEqual(self.result(), value, places, delta=delta)
def assertResultGreater(self, value):
"""
Just like self.assertTrue(self.result() > value), but with a nicer
default message.
Equivalent to `assertGreater(self, result(), value)`.
"""
self.assertGreater(self.result(), value)
def assertResultGreaterEqual(self, value):
"""
Just like self.assertTrue(self.result() >= value), but with a nicer
default message.
Equivalent to `assertGreaterEqual(self.result(), value)`.
"""
self.assertGreaterEqual(self.result(), value)
def assertResultLess(self, value):
"""
Just like self.assertTrue(self.result() < value), but with a nicer
default message.
Equivalent to `assertLess(self.result(), value)`.
"""
self.assertLess(self.result(), value)
def assertResultLessEqual(self, value):
"""
Just like self.assertTrue(self.result() <= value), but with a nicer
default message.
Equivalent to `assertLessEqual(self.result(), value)`.
"""
self.assertLessEqual(self.result(), value)
def assertResultRegex(self, expected_regex):
"""
Fail the test unless the result matches the regular expression.
Equivalent to `self.assertRegex(self.result(), expected_regex)`.
"""
self.assertRegex(self.result(), expected_regex)
def assertResultNotRegex(self, unexpected_regex):
"""
Fail the test if the result matches the regular expression.
Equivalent to `assertNotRegex(self.result(), unexpected_regex)`.
"""
self.assertNotRegex(self.result(), unexpected_regex)
def assertResultCount(self, iterable):
"""
Assert that the result has the same elements as the iterable without
regard to order.
Equivalent to `assertCountEqual(self.result(), iterable).`
"""
self.assertCountEqual(self.result(), iterable)
def assertResultList(self, lst):
"""
Assert that the result is equal to lst.
Equivalent to `assertListEqual(self.result(), lst)`.
"""
self.assertListEqual(self.result(), lst)
def assertResultTuple(self, tpl):
"""
Assert that the result is equal to tpl.
Equivalent to `self.assertTupleEqual(self.result(), tpl)`.
"""
self.assertTupleEqual(self.result(), tpl)
def assertResultSet(self, st):
"""
Assert that the result is equal to st.
Equivalent to `self.assertSetEqual(self.result(), st)`.
"""
self.assertSetEqual(self.result(), st)
def assertResultDict(self, dct):
"""
Assert that the result is equal to dct.
Equivalent to `assertDictEqual(self.result(), dct)`.
"""
self.assertDictEqual(self.result(), dct)
def _callTestMethod(self, method):
if hasattr(method, "_subjectKwargs"):
self._subjectKwargs = method._subjectKwargs
else:
self._subjectKwargs = {}
if method() is not None:
warn(
f"It is deprecated to return a value that is not None from a "
f"test case ({method})",
DeprecationWarning,
stacklevel=3,
)
self._subjectKwargs = {}