|
1 mocksignature |
|
2 ============= |
|
3 |
|
4 .. currentmodule:: mock |
|
5 |
|
6 .. note:: |
|
7 |
|
8 :ref:`auto-speccing`, added in mock 0.8, is a more advanced version of |
|
9 `mocksignature` and can be used for many of the same use cases. |
|
10 |
|
11 A problem with using mock objects to replace real objects in your tests is that |
|
12 :class:`Mock` can be *too* flexible. Your code can treat the mock objects in |
|
13 any way and you have to manually check that they were called correctly. If your |
|
14 code calls functions or methods with the wrong number of arguments then mocks |
|
15 don't complain. |
|
16 |
|
17 The solution to this is `mocksignature`, which creates functions with the |
|
18 same signature as the original, but delegating to a mock. You can interrogate |
|
19 the mock in the usual way to check it has been called with the *right* |
|
20 arguments, but if it is called with the wrong number of arguments it will |
|
21 raise a `TypeError` in the same way your production code would. |
|
22 |
|
23 Another advantage is that your mocked objects are real functions, which can |
|
24 be useful when your code uses |
|
25 `inspect <http://docs.python.org/library/inspect.html>`_ or depends on |
|
26 functions being function objects. |
|
27 |
|
28 .. function:: mocksignature(func, mock=None, skipfirst=False) |
|
29 |
|
30 Create a new function with the same signature as `func` that delegates |
|
31 to `mock`. If `skipfirst` is True the first argument is skipped, useful |
|
32 for methods where `self` needs to be omitted from the new function. |
|
33 |
|
34 If you don't pass in a `mock` then one will be created for you. |
|
35 |
|
36 Functions returned by `mocksignature` have many of the same attributes |
|
37 and assert methods as a mock object. |
|
38 |
|
39 The mock is set as the `mock` attribute of the returned function for easy |
|
40 access. |
|
41 |
|
42 `mocksignature` can also be used with classes. It copies the signature of |
|
43 the `__init__` method. |
|
44 |
|
45 When used with callable objects (instances) it copies the signature of the |
|
46 `__call__` method. |
|
47 |
|
48 `mocksignature` will work out if it is mocking the signature of a method on |
|
49 an instance or a method on a class and do the "right thing" with the `self` |
|
50 argument in both cases. |
|
51 |
|
52 Because of a limitation in the way that arguments are collected by functions |
|
53 created by `mocksignature` they are *always* passed as positional arguments |
|
54 (including defaults) and not keyword arguments. |
|
55 |
|
56 |
|
57 mocksignature api |
|
58 ----------------- |
|
59 |
|
60 Although the objects returned by `mocksignature` api are real function objects, |
|
61 they have much of the same api as the :class:`Mock` class. This includes the |
|
62 assert methods: |
|
63 |
|
64 .. doctest:: |
|
65 |
|
66 >>> def func(a, b, c): |
|
67 ... pass |
|
68 ... |
|
69 >>> func2 = mocksignature(func) |
|
70 >>> func2.called |
|
71 False |
|
72 >>> func2.return_value = 3 |
|
73 >>> func2(1, 2, 3) |
|
74 3 |
|
75 >>> func2.called |
|
76 True |
|
77 >>> func2.assert_called_once_with(1, 2, 3) |
|
78 >>> func2.assert_called_with(1, 2, 4) |
|
79 Traceback (most recent call last): |
|
80 ... |
|
81 AssertionError: Expected call: mock(1, 2, 4) |
|
82 Actual call: mock(1, 2, 3) |
|
83 >>> func2.call_count |
|
84 1 |
|
85 >>> func2.side_effect = IndexError |
|
86 >>> func2(4, 5, 6) |
|
87 Traceback (most recent call last): |
|
88 ... |
|
89 IndexError |
|
90 |
|
91 The mock object that is being delegated to is available as the `mock` attribute |
|
92 of the function created by `mocksignature`. |
|
93 |
|
94 .. doctest:: |
|
95 |
|
96 >>> func2.mock.mock_calls |
|
97 [call(1, 2, 3), call(4, 5, 6)] |
|
98 |
|
99 The methods and attributes available on functions returned by `mocksignature` |
|
100 are: |
|
101 |
|
102 :meth:`~Mock.assert_any_call`, :meth:`~Mock.assert_called_once_with`, |
|
103 :meth:`~Mock.assert_called_with`, :meth:`~Mock.assert_has_calls`, |
|
104 :attr:`~Mock.call_args`, :attr:`~Mock.call_args_list`, |
|
105 :attr:`~Mock.call_count`, :attr:`~Mock.called`, |
|
106 :attr:`~Mock.method_calls`, `mock`, :attr:`~Mock.mock_calls`, |
|
107 :meth:`~Mock.reset_mock`, :attr:`~Mock.return_value`, and |
|
108 :attr:`~Mock.side_effect`. |
|
109 |
|
110 |
|
111 Example use |
|
112 ----------- |
|
113 |
|
114 Basic use |
|
115 ~~~~~~~~~ |
|
116 |
|
117 .. doctest:: |
|
118 |
|
119 >>> def function(a, b, c=None): |
|
120 ... pass |
|
121 ... |
|
122 >>> mock = Mock() |
|
123 >>> function = mocksignature(function, mock) |
|
124 >>> function() |
|
125 Traceback (most recent call last): |
|
126 ... |
|
127 TypeError: <lambda>() takes at least 2 arguments (0 given) |
|
128 >>> function.return_value = 'some value' |
|
129 >>> function(1, 2, 'foo') |
|
130 'some value' |
|
131 >>> function.assert_called_with(1, 2, 'foo') |
|
132 |
|
133 |
|
134 Keyword arguments |
|
135 ~~~~~~~~~~~~~~~~~ |
|
136 |
|
137 Note that arguments to functions created by `mocksignature` are always passed |
|
138 in to the underlying mock by position even when called with keywords: |
|
139 |
|
140 .. doctest:: |
|
141 |
|
142 >>> def function(a, b, c=None): |
|
143 ... pass |
|
144 ... |
|
145 >>> function = mocksignature(function) |
|
146 >>> function.return_value = None |
|
147 >>> function(1, 2) |
|
148 >>> function.assert_called_with(1, 2, None) |
|
149 |
|
150 |
|
151 Mocking methods and self |
|
152 ~~~~~~~~~~~~~~~~~~~~~~~~ |
|
153 |
|
154 When you use `mocksignature` to replace a method on a class then `self` |
|
155 will be included in the method signature - and you will need to include |
|
156 the instance when you do your asserts. |
|
157 |
|
158 As a curious factor of the way Python (2) wraps methods fetched from a class, |
|
159 we can *get* the `return_value` from a function set on a class, but we can't |
|
160 set it. We have to do this through the exposed `mock` attribute instead: |
|
161 |
|
162 .. doctest:: |
|
163 |
|
164 >>> class SomeClass(object): |
|
165 ... def method(self, a, b, c=None): |
|
166 ... pass |
|
167 ... |
|
168 >>> SomeClass.method = mocksignature(SomeClass.method) |
|
169 >>> SomeClass.method.mock.return_value = None |
|
170 >>> instance = SomeClass() |
|
171 >>> instance.method() |
|
172 Traceback (most recent call last): |
|
173 ... |
|
174 TypeError: <lambda>() takes at least 4 arguments (1 given) |
|
175 >>> instance.method(1, 2, 3) |
|
176 >>> instance.method.assert_called_with(instance, 1, 2, 3) |
|
177 |
|
178 When you use `mocksignature` on instance methods `self` isn't included (and we |
|
179 can set the `return_value` etc directly): |
|
180 |
|
181 .. doctest:: |
|
182 |
|
183 >>> class SomeClass(object): |
|
184 ... def method(self, a, b, c=None): |
|
185 ... pass |
|
186 ... |
|
187 >>> instance = SomeClass() |
|
188 >>> instance.method = mocksignature(instance.method) |
|
189 >>> instance.method.return_value = None |
|
190 >>> instance.method(1, 2, 3) |
|
191 >>> instance.method.assert_called_with(1, 2, 3) |
|
192 |
|
193 |
|
194 mocksignature with classes |
|
195 ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
196 |
|
197 When used with a class `mocksignature` copies the signature of the `__init__` |
|
198 method. |
|
199 |
|
200 .. doctest:: |
|
201 |
|
202 >>> class Something(object): |
|
203 ... def __init__(self, foo, bar): |
|
204 ... pass |
|
205 ... |
|
206 >>> MockSomething = mocksignature(Something) |
|
207 >>> instance = MockSomething(10, 9) |
|
208 >>> assert instance is MockSomething.return_value |
|
209 >>> MockSomething.assert_called_with(10, 9) |
|
210 >>> MockSomething() |
|
211 Traceback (most recent call last): |
|
212 ... |
|
213 TypeError: <lambda>() takes at least 2 arguments (0 given) |
|
214 |
|
215 Because the object returned by `mocksignature` is a function rather than a |
|
216 `Mock` you lose the other capabilities of `Mock`, like dynamic attribute |
|
217 creation. |
|
218 |
|
219 |
|
220 mocksignature with callable objects |
|
221 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
222 |
|
223 When used with a callable object `mocksignature` copies the signature of the |
|
224 `__call__` method. |
|
225 |
|
226 .. doctest:: |
|
227 |
|
228 >>> class Something(object): |
|
229 ... def __call__(self, spam, eggs): |
|
230 ... pass |
|
231 ... |
|
232 >>> something = Something() |
|
233 >>> mock_something = mocksignature(something) |
|
234 >>> result = mock_something(10, 9) |
|
235 >>> mock_something.assert_called_with(10, 9) |
|
236 >>> mock_something() |
|
237 Traceback (most recent call last): |
|
238 ... |
|
239 TypeError: <lambda>() takes at least 2 arguments (0 given) |
|
240 |
|
241 |
|
242 mocksignature argument to patch |
|
243 ------------------------------- |
|
244 |
|
245 `mocksignature` is available as a keyword argument to :func:`patch` or |
|
246 :func:`patch.object`. It can be used with functions / methods / classes and |
|
247 callable objects. |
|
248 |
|
249 .. doctest:: |
|
250 |
|
251 >>> class SomeClass(object): |
|
252 ... def method(self, a, b, c=None): |
|
253 ... pass |
|
254 ... |
|
255 >>> @patch.object(SomeClass, 'method', mocksignature=True) |
|
256 ... def test(mock_method): |
|
257 ... instance = SomeClass() |
|
258 ... mock_method.return_value = None |
|
259 ... instance.method(1, 2) |
|
260 ... mock_method.assert_called_with(instance, 1, 2, None) |
|
261 ... |
|
262 >>> test() |