• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2from unittest import mock
3
4
5class SampleObject:
6    def __init__(self):
7        self.attr_sample1 = 1
8        self.attr_sample2 = 1
9
10    def method_sample1(self):
11        pass
12
13    def method_sample2(self):
14        pass
15
16
17class TestSealable(unittest.TestCase):
18
19    def test_attributes_return_more_mocks_by_default(self):
20        m = mock.Mock()
21
22        self.assertIsInstance(m.test, mock.Mock)
23        self.assertIsInstance(m.test(), mock.Mock)
24        self.assertIsInstance(m.test().test2(), mock.Mock)
25
26    def test_new_attributes_cannot_be_accessed_on_seal(self):
27        m = mock.Mock()
28
29        mock.seal(m)
30        with self.assertRaises(AttributeError):
31            m.test
32        with self.assertRaises(AttributeError):
33            m()
34
35    def test_new_attributes_cannot_be_set_on_seal(self):
36        m = mock.Mock()
37
38        mock.seal(m)
39        with self.assertRaises(AttributeError):
40            m.test = 1
41
42    def test_existing_attributes_can_be_set_on_seal(self):
43        m = mock.Mock()
44        m.test.test2 = 1
45
46        mock.seal(m)
47        m.test.test2 = 2
48        self.assertEqual(m.test.test2, 2)
49
50    def test_new_attributes_cannot_be_set_on_child_of_seal(self):
51        m = mock.Mock()
52        m.test.test2 = 1
53
54        mock.seal(m)
55        with self.assertRaises(AttributeError):
56            m.test.test3 = 1
57
58    def test_existing_attributes_allowed_after_seal(self):
59        m = mock.Mock()
60
61        m.test.return_value = 3
62
63        mock.seal(m)
64        self.assertEqual(m.test(), 3)
65
66    def test_initialized_attributes_allowed_after_seal(self):
67        m = mock.Mock(test_value=1)
68
69        mock.seal(m)
70        self.assertEqual(m.test_value, 1)
71
72    def test_call_on_sealed_mock_fails(self):
73        m = mock.Mock()
74
75        mock.seal(m)
76        with self.assertRaises(AttributeError):
77            m()
78
79    def test_call_on_defined_sealed_mock_succeeds(self):
80        m = mock.Mock(return_value=5)
81
82        mock.seal(m)
83        self.assertEqual(m(), 5)
84
85    def test_seals_recurse_on_added_attributes(self):
86        m = mock.Mock()
87
88        m.test1.test2().test3 = 4
89
90        mock.seal(m)
91        self.assertEqual(m.test1.test2().test3, 4)
92        with self.assertRaises(AttributeError):
93            m.test1.test2().test4
94        with self.assertRaises(AttributeError):
95            m.test1.test3
96
97    def test_seals_recurse_on_magic_methods(self):
98        m = mock.MagicMock()
99
100        m.test1.test2["a"].test3 = 4
101        m.test1.test3[2:5].test3 = 4
102
103        mock.seal(m)
104        self.assertEqual(m.test1.test2["a"].test3, 4)
105        self.assertEqual(m.test1.test2[2:5].test3, 4)
106        with self.assertRaises(AttributeError):
107            m.test1.test2["a"].test4
108        with self.assertRaises(AttributeError):
109            m.test1.test3[2:5].test4
110
111    def test_seals_dont_recurse_on_manual_attributes(self):
112        m = mock.Mock(name="root_mock")
113
114        m.test1.test2 = mock.Mock(name="not_sealed")
115        m.test1.test2.test3 = 4
116
117        mock.seal(m)
118        self.assertEqual(m.test1.test2.test3, 4)
119        m.test1.test2.test4  # Does not raise
120        m.test1.test2.test4 = 1  # Does not raise
121
122    def test_integration_with_spec_att_definition(self):
123        """You are not restricted when using mock with spec"""
124        m = mock.Mock(SampleObject)
125
126        m.attr_sample1 = 1
127        m.attr_sample3 = 3
128
129        mock.seal(m)
130        self.assertEqual(m.attr_sample1, 1)
131        self.assertEqual(m.attr_sample3, 3)
132        with self.assertRaises(AttributeError):
133            m.attr_sample2
134
135    def test_integration_with_spec_method_definition(self):
136        """You need to defin the methods, even if they are in the spec"""
137        m = mock.Mock(SampleObject)
138
139        m.method_sample1.return_value = 1
140
141        mock.seal(m)
142        self.assertEqual(m.method_sample1(), 1)
143        with self.assertRaises(AttributeError):
144            m.method_sample2()
145
146    def test_integration_with_spec_method_definition_respects_spec(self):
147        """You cannot define methods out of the spec"""
148        m = mock.Mock(SampleObject)
149
150        with self.assertRaises(AttributeError):
151            m.method_sample3.return_value = 3
152
153    def test_sealed_exception_has_attribute_name(self):
154        m = mock.Mock()
155
156        mock.seal(m)
157        with self.assertRaises(AttributeError) as cm:
158            m.SECRETE_name
159        self.assertIn("SECRETE_name", str(cm.exception))
160
161    def test_attribute_chain_is_maintained(self):
162        m = mock.Mock(name="mock_name")
163        m.test1.test2.test3.test4
164
165        mock.seal(m)
166        with self.assertRaises(AttributeError) as cm:
167            m.test1.test2.test3.test4.boom
168        self.assertIn("mock_name.test1.test2.test3.test4.boom", str(cm.exception))
169
170    def test_call_chain_is_maintained(self):
171        m = mock.Mock()
172        m.test1().test2.test3().test4
173
174        mock.seal(m)
175        with self.assertRaises(AttributeError) as cm:
176            m.test1().test2.test3().test4()
177        self.assertIn("mock.test1().test2.test3().test4", str(cm.exception))
178
179
180if __name__ == "__main__":
181    unittest.main()
182