python/mock-1.0.0/html/_sources/mocksignature.txt

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial