Skip to content

Commit 64e640b

Browse files
Add files via upload
1 parent 07c2108 commit 64e640b

File tree

4 files changed

+398
-0
lines changed

4 files changed

+398
-0
lines changed

assertions/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from .assertions import *
2+
3+
__version__ = "1.0"
4+
5+
__all__ = [
6+
"__version__",
7+
"equals",
8+
"not_equals",
9+
"expect",
10+
"raises",
11+
"does_not_raise",
12+
"approximately_equals",
13+
"not_approximately_equals",
14+
"contains",
15+
"does_not_contain",
16+
"is_instance",
17+
"not_is_instance",
18+
"greater",
19+
"less",
20+
]

assertions/assertions.py

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
"""A small unit testing library for Python."""
2+
3+
from typing import Callable, Tuple, Iterable, Type, Any
4+
5+
__all__ = [
6+
"equals",
7+
"not_equals",
8+
"expect",
9+
"raises",
10+
"does_not_raise",
11+
"approximately_equals",
12+
"not_approximately_equals",
13+
"contains",
14+
"does_not_contain",
15+
"is_instance",
16+
"not_is_instance",
17+
"greater",
18+
"less",
19+
]
20+
21+
def equals(
22+
a: Any,
23+
b: Any,
24+
message_on_fail: str = "Test failed",
25+
verbose: bool = False,
26+
) -> None:
27+
"""
28+
Assertion of strict equality.
29+
30+
:param a: The first value being compared.
31+
:param b: The second value being compared.
32+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
33+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
34+
"""
35+
36+
if verbose and a == b:
37+
print("Test passed")
38+
39+
elif a != b:
40+
raise AssertionError(message_on_fail + f": expected {a} to equal {b}")
41+
42+
def not_equals(
43+
a: Any,
44+
b: Any,
45+
message_on_fail: str = "Test failed",
46+
verbose: bool = False,
47+
) -> None:
48+
"""
49+
Assertion of strict inequality.
50+
51+
:param a: The first value being compared.
52+
:param b: The second value being compared.
53+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
54+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
55+
"""
56+
57+
if verbose and a != b:
58+
print("Test passed")
59+
60+
elif a == b:
61+
raise AssertionError(message_on_fail + f": expected {a} to not equal {b}")
62+
63+
def expect(
64+
value: Any,
65+
message_on_fail: str = "Test failed",
66+
verbose: bool = False,
67+
) -> None:
68+
"""
69+
Assertion of truthiness.
70+
71+
:param value: The value being tested.
72+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
73+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
74+
"""
75+
76+
if verbose and value:
77+
print("Test passed")
78+
79+
elif not value:
80+
raise AssertionError(message_on_fail)
81+
82+
83+
# noinspection PyBroadException
84+
def raises(
85+
function: Callable[..., Any],
86+
exceptions: Tuple[Type[BaseException], ...] | Type[BaseException] = (Exception,),
87+
message_on_fail = "Test failed",
88+
verbose = False,
89+
) -> None:
90+
"""
91+
Assertion of a function raising an exception.
92+
93+
:param function: The function being tested.
94+
:param exceptions: The exception type(s) being tested. Default value is (Exception,).
95+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
96+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
97+
"""
98+
99+
if not isinstance(exceptions, tuple):
100+
exceptions = (exceptions,)
101+
102+
def handle():
103+
raise AssertionError(
104+
message_on_fail
105+
+ ": expected given function to raise "
106+
+ ("" if len(exceptions) == 1 else "any of the following: ")
107+
+ ", ".join(map(lambda e: e.__name__, exceptions))
108+
)
109+
110+
try:
111+
function()
112+
113+
except exceptions:
114+
if verbose:
115+
print("Test passed")
116+
117+
return None
118+
119+
except:
120+
# handle cases when the exception is not of the expected type
121+
handle()
122+
123+
else:
124+
handle()
125+
126+
127+
# noinspection PyBroadException
128+
def does_not_raise(
129+
function: Callable[..., Any],
130+
exceptions: Tuple[Type[BaseException], ...] | Type[BaseException] = (Exception,),
131+
message_on_fail: str = "Test failed",
132+
verbose: bool = False,
133+
) -> None:
134+
"""
135+
Assertion of a function not raising an exception.
136+
137+
:param function: The function being tested.
138+
:param exceptions: The exception type(s) being tested. Default value is (Exception,).
139+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
140+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
141+
"""
142+
143+
if not isinstance(exceptions, tuple):
144+
exceptions = (exceptions,)
145+
146+
def handle():
147+
raise AssertionError(
148+
message_on_fail
149+
+ ": expected given function to not raise "
150+
+ ("" if len(exceptions) == 1 else "any of the following: ")
151+
+ ", ".join(map(lambda e: e.__name__, exceptions))
152+
)
153+
154+
try:
155+
function()
156+
157+
except exceptions:
158+
handle()
159+
160+
except:
161+
# handle cases when the exception is not of the expected type
162+
if verbose:
163+
print("Test passed")
164+
165+
else:
166+
if verbose:
167+
print("Test passed")
168+
169+
def approximately_equals(
170+
a: int | float,
171+
b: int | float,
172+
margin: int | float,
173+
message_on_fail: str = "Test failed",
174+
verbose: bool = False,
175+
) -> None:
176+
"""
177+
Assertion of approximate equality.
178+
179+
:param a: The first value being compared.
180+
:param b: The second value being compared.
181+
:param margin: The margin of error allowed for the comparison.
182+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
183+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
184+
"""
185+
186+
if verbose and abs(a-b) <= margin:
187+
print("Test passed")
188+
189+
elif abs(a-b) > margin:
190+
raise AssertionError(message_on_fail + f": expected {a} to be within {margin} of {b}")
191+
192+
def not_approximately_equals(
193+
a: int | float,
194+
b: int | float,
195+
margin: int | float,
196+
message_on_fail: str = "Test failed",
197+
verbose: bool = False,
198+
) -> None:
199+
"""
200+
Assertion of approximate inequality.
201+
202+
:param a: The first value being compared.
203+
:param b: The second value being compared.
204+
:param margin: The margin of error allowed for the comparison.
205+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
206+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
207+
"""
208+
209+
if verbose and abs(a-b) > margin:
210+
print("Test passed")
211+
212+
elif abs(a-b) <= margin:
213+
raise AssertionError(message_on_fail + f": expected {a} to not be within {margin} of {b}")
214+
215+
def contains(
216+
it: Iterable[Any],
217+
value: Any,
218+
message_on_fail: str = "Test failed",
219+
verbose: bool = False,
220+
) -> None:
221+
"""
222+
Assertion of the given iterable containing the given value.
223+
224+
:param it: The iterable being tested.
225+
:param value: The value being tested for containment.
226+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
227+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
228+
"""
229+
230+
if verbose and value in it:
231+
print("Test passed")
232+
233+
elif value not in it:
234+
raise AssertionError(message_on_fail + f": expected {value} to be in {it}")
235+
236+
def does_not_contain(
237+
it: Iterable[Any],
238+
value: Any,
239+
message_on_fail: str = "Test failed",
240+
verbose: bool = False,
241+
) -> None:
242+
"""
243+
Assertion of the given iterable not containing the given value.
244+
245+
:param it: The iterable being tested.
246+
:param value: The value being tested for containment.
247+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
248+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
249+
"""
250+
251+
if verbose and value not in it:
252+
print("Test passed")
253+
254+
elif value in it:
255+
raise AssertionError(message_on_fail + f": expected {value} to not be in {it}")
256+
257+
def is_instance(
258+
value: Any,
259+
types: Type[Any] | Tuple[Type[Any], ...],
260+
message_on_fail: str = "Test failed",
261+
verbose: bool = False,
262+
) -> None:
263+
"""
264+
Assertion of the given value being an instance of the given type(s) or class(es).
265+
266+
:param value: The value being tested.
267+
:param types: The type(s) or class(es) being tested.
268+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
269+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
270+
"""
271+
if not isinstance(types, tuple):
272+
types = (types,)
273+
274+
if verbose and isinstance(value, types):
275+
print("Test passed")
276+
277+
elif not isinstance(value, types):
278+
raise AssertionError(message_on_fail + f": expected {value} to be an instance of {types}")
279+
280+
def not_is_instance(
281+
value: Any,
282+
types: Type[Any] | Tuple[Type[Any], ...],
283+
message_on_fail: str = "Test failed",
284+
verbose: bool = False,
285+
) -> None:
286+
"""
287+
Assertion of the given value not being an instance of the given type(s) or class(es).
288+
289+
:param value: The value being tested.
290+
:param types: The type(s) or class(es) being tested.
291+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
292+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
293+
"""
294+
if not isinstance(types, tuple):
295+
types = (types,)
296+
297+
if verbose and not isinstance(value, types):
298+
print("Test passed")
299+
300+
elif isinstance(value, types):
301+
raise AssertionError(message_on_fail + f": expected {value} to not be an instance of {types}")
302+
303+
def greater(
304+
value: int | float,
305+
comparison: int | float,
306+
message_on_fail: str = "Test failed",
307+
verbose: bool = False,
308+
) -> None:
309+
"""
310+
Assertion of the given value being greater than the given comparison value.
311+
312+
:param value: The value being tested.
313+
:param comparison: The comparison value.
314+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
315+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
316+
"""
317+
318+
if verbose and value > comparison:
319+
print("Test passed")
320+
321+
elif value <= comparison:
322+
raise AssertionError(message_on_fail + f": expected {value} to be greater than {comparison}")
323+
324+
def less(
325+
value: int | float,
326+
comparison: int | float,
327+
message_on_fail: str = "Test failed",
328+
verbose: bool = False,
329+
) -> None:
330+
"""
331+
Assertion of the given value being less than the given comparison value.
332+
333+
:param value: The value being tested.
334+
:param comparison: The comparison value.
335+
:param message_on_fail: Message to display in case of assertion failure. Default value is "Test failed".
336+
:param verbose: Whether the function should print a message in case of assertion success. Default value is False.
337+
"""
338+
339+
if verbose and value < comparison:
340+
print("Test passed")
341+
342+
elif value >= comparison:
343+
raise AssertionError(message_on_fail + f": expected {value} to be less than {comparison}")

setup.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from setuptools import setup, find_packages
2+
from pathlib import Path
3+
4+
script_dir = Path(__file__).parent
5+
6+
def get_version():
7+
with (script_dir / "assertions" / "__init__.py").open("r") as f:
8+
for line in f:
9+
if line.startswith("__version__"):
10+
return line.split("=")[1].strip().strip('"')
11+
12+
setup(
13+
name="assertions",
14+
version=get_version(),
15+
packages=find_packages(),
16+
install_requires=[],
17+
)

0 commit comments

Comments
 (0)