1import unittest
2
3import solution
4
5
6class TestSanity(unittest.TestCase):
7 """Check if the function is present."""
8
9 def test_interfaces(self):
10 self.assertIn('type_check', dir(solution), 'Убеди се, че функцията "type_check" е налична с точно това име.')
11 self.assertTrue(callable(solution.type_check), 'Убеди се, че "type_check" е функция.')
12
13
14if __name__ == '__main__':
15 unittest.main()
1import itertools
2import unittest
3from unittest.mock import call, patch
4
5from solution import type_check
6
7
8def join_collections(*collections):
9 result = []
10 for collection in collections:
11 result.extend(collection)
12 return result
13
14def nothing():
15 pass
16
17def lonely_island(main_character, supporting_character, **kwargs):
18 return f"This is the tale of {main_character} and {supporting_character}!"
19
20def divide_by_zero(numbers):
21 return [number / 0 for number in numbers]
22
23
24class TestTypeCheck(unittest.TestCase):
25 """Test the function of the decorator."""
26
27 BASE_STRING_IN = "Invalid input arguments, expected {}!"
28 BASE_STRING_OUT = "Invalid output value, expected {}!"
29
30 def assert_in_permutations(self, call, types, inout_string):
31 """Check if call is in a list of all permutations of the types."""
32 types_permutations = itertools.permutations(types)
33 types_strings = [", ".join(str(_type) for _type in perm) for perm in types_permutations]
34 call_permutations = [call(inout_string.format(types_string)) for types_string in types_strings]
35 self.assertIn(call, call_permutations)
36
37 @patch("builtins.print")
38 def test_check_in(self, mock_print):
39 """The decorator should report invalid "in" arguments."""
40 # Single type
41 decorated = type_check("in")(list)(join_collections)
42 result = decorated('asdf', 'movie')
43 self.assertEqual(mock_print.call_count, 1)
44 mock_print.assert_has_calls(
45 [call(self.BASE_STRING_IN.format(str(list)))])
46 self.assertEqual(result, ['a', 's', 'd', 'f', 'm', 'o', 'v', 'i', 'e'])
47
48 # Multiple types
49 mock_print.reset_mock()
50 types = (list, tuple, set, type(_ for _ in []))
51 decorated = type_check("in")(*types)(join_collections)
52 result = decorated('asdf', 'movie')
53 self.assertEqual(mock_print.call_count, 1)
54 self.assert_in_permutations(mock_print.mock_calls[0], types, self.BASE_STRING_IN)
55 self.assertEqual(result, ['a', 's', 'd', 'f', 'm', 'o', 'v', 'i', 'e'])
56
57 # Valid input
58 mock_print.reset_mock()
59 decorated = type_check("in")(list, tuple, set, type(_ for _ in []))(join_collections)
60 result = decorated([1, 2], (3,), {4})
61 mock_print.assert_not_called()
62 self.assertEqual(result, [1, 2, 3, 4])
63
64 @patch("builtins.print")
65 def test_check_out(self, mock_print):
66 """The decorator should report an invalid "out" value."""
67 # Single type
68 decorated = type_check("out")(
69 type("Epyt", (type,),{"__repr__": lambda self: f"{self.__name__[::-1].lower()} {self.__class__.__name__[::-1].lower()}"})("Ym", (), {}))(nothing)
70 # Why do I do these things?
71 result = decorated()
72 mock_print.assert_has_calls([call(self.BASE_STRING_OUT.format("my type"))])
73 self.assertEqual(mock_print.call_count, 1)
74 self.assertEqual(result, None)
75
76 # Multiple types
77 mock_print.reset_mock()
78 types = (str, bool, Exception)
79 decorated = type_check("out")(*types)(nothing)
80 result = decorated()
81 self.assertEqual(mock_print.call_count, 1)
82 self.assert_in_permutations(mock_print.mock_calls[0], types, self.BASE_STRING_OUT)
83 self.assertEqual(result, None)
84
85 # Valid output
86 mock_print.reset_mock()
87 decorated = type_check("out")(type(None))(nothing)
88 result = decorated()
89 mock_print.assert_not_called()
90 self.assertEqual(result, None)
91
92 @patch("builtins.print")
93 def test_check_both(self, mock_print):
94 """The decorator should report invalid "in" and "out" together."""
95 decorated = type_check("in")(float)(lonely_island)
96 decorated = type_check("out")(int)(decorated)
97 result = decorated("Captain Jack Sparrow", "Bill", pirates=True)
98 mock_print.assert_has_calls(
99 [call(self.BASE_STRING_IN.format(str(float))),
100 call(self.BASE_STRING_OUT.format(str(int)))])
101 self.assertEqual(mock_print.call_count, 2)
102 self.assertEqual(result, "This is the tale of Captain Jack Sparrow and Bill!")
103
104 @patch("builtins.print")
105 def test_check_decorated_exception(self, mock_print):
106 """The decorator should not supress any exceptions raised."""
107 types = (list, tuple)
108 decorated = type_check("in")(*types)(divide_by_zero)
109 decorated = type_check("out")(*types)(decorated)
110 with self.assertRaises(ZeroDivisionError):
111 decorated({1, 2, 3, 4, 5, 6})
112 self.assert_in_permutations(mock_print.mock_calls[0], types, self.BASE_STRING_IN)
113
114if __name__ == '__main__':
115 unittest.main()
Василена Станойска
23.10.2024 10:04Здравейте. В `@type_check("in")(int, float)` трябва да подадем като възможни типове толкова на брой типове, колкото са и аргументите на функцията, която декорираме или може да подадем само един тип, например int и трябва всички аргументи на power (примерно) да проверим дали са int?
|