| mocksignature |
| ============= |
| |
| .. currentmodule:: mock |
| |
| .. note:: |
| |
| :ref:`auto-speccing`, added in mock 0.8, is a more advanced version of |
| `mocksignature` and can be used for many of the same use cases. |
| |
| A problem with using mock objects to replace real objects in your tests is that |
| :class:`Mock` can be *too* flexible. Your code can treat the mock objects in |
| any way and you have to manually check that they were called correctly. If your |
| code calls functions or methods with the wrong number of arguments then mocks |
| don't complain. |
| |
| The solution to this is `mocksignature`, which creates functions with the |
| same signature as the original, but delegating to a mock. You can interrogate |
| the mock in the usual way to check it has been called with the *right* |
| arguments, but if it is called with the wrong number of arguments it will |
| raise a `TypeError` in the same way your production code would. |
| |
| Another advantage is that your mocked objects are real functions, which can |
| be useful when your code uses |
| `inspect <http://docs.python.org/library/inspect.html>`_ or depends on |
| functions being function objects. |
| |
| .. function:: mocksignature(func, mock=None, skipfirst=False) |
| |
| Create a new function with the same signature as `func` that delegates |
| to `mock`. If `skipfirst` is True the first argument is skipped, useful |
| for methods where `self` needs to be omitted from the new function. |
| |
| If you don't pass in a `mock` then one will be created for you. |
| |
| Functions returned by `mocksignature` have many of the same attributes |
| and assert methods as a mock object. |
| |
| The mock is set as the `mock` attribute of the returned function for easy |
| access. |
| |
| `mocksignature` can also be used with classes. It copies the signature of |
| the `__init__` method. |
| |
| When used with callable objects (instances) it copies the signature of the |
| `__call__` method. |
| |
| `mocksignature` will work out if it is mocking the signature of a method on |
| an instance or a method on a class and do the "right thing" with the `self` |
| argument in both cases. |
| |
| Because of a limitation in the way that arguments are collected by functions |
| created by `mocksignature` they are *always* passed as positional arguments |
| (including defaults) and not keyword arguments. |
| |
| |
| mocksignature api |
| ----------------- |
| |
| Although the objects returned by `mocksignature` api are real function objects, |
| they have much of the same api as the :class:`Mock` class. This includes the |
| assert methods: |
| |
| .. doctest:: |
| |
| >>> def func(a, b, c): |
| ... pass |
| ... |
| >>> func2 = mocksignature(func) |
| >>> func2.called |
| False |
| >>> func2.return_value = 3 |
| >>> func2(1, 2, 3) |
| 3 |
| >>> func2.called |
| True |
| >>> func2.assert_called_once_with(1, 2, 3) |
| >>> func2.assert_called_with(1, 2, 4) |
| Traceback (most recent call last): |
| ... |
| AssertionError: Expected call: mock(1, 2, 4) |
| Actual call: mock(1, 2, 3) |
| >>> func2.call_count |
| 1 |
| >>> func2.side_effect = IndexError |
| >>> func2(4, 5, 6) |
| Traceback (most recent call last): |
| ... |
| IndexError |
| |
| The mock object that is being delegated to is available as the `mock` attribute |
| of the function created by `mocksignature`. |
| |
| .. doctest:: |
| |
| >>> func2.mock.mock_calls |
| [call(1, 2, 3), call(4, 5, 6)] |
| |
| The methods and attributes available on functions returned by `mocksignature` |
| are: |
| |
| :meth:`~Mock.assert_any_call`, :meth:`~Mock.assert_called_once_with`, |
| :meth:`~Mock.assert_called_with`, :meth:`~Mock.assert_has_calls`, |
| :attr:`~Mock.call_args`, :attr:`~Mock.call_args_list`, |
| :attr:`~Mock.call_count`, :attr:`~Mock.called`, |
| :attr:`~Mock.method_calls`, `mock`, :attr:`~Mock.mock_calls`, |
| :meth:`~Mock.reset_mock`, :attr:`~Mock.return_value`, and |
| :attr:`~Mock.side_effect`. |
| |
| |
| Example use |
| ----------- |
| |
| Basic use |
| ~~~~~~~~~ |
| |
| .. doctest:: |
| |
| >>> def function(a, b, c=None): |
| ... pass |
| ... |
| >>> mock = Mock() |
| >>> function = mocksignature(function, mock) |
| >>> function() |
| Traceback (most recent call last): |
| ... |
| TypeError: <lambda>() takes at least 2 arguments (0 given) |
| >>> function.return_value = 'some value' |
| >>> function(1, 2, 'foo') |
| 'some value' |
| >>> function.assert_called_with(1, 2, 'foo') |
| |
| |
| Keyword arguments |
| ~~~~~~~~~~~~~~~~~ |
| |
| Note that arguments to functions created by `mocksignature` are always passed |
| in to the underlying mock by position even when called with keywords: |
| |
| .. doctest:: |
| |
| >>> def function(a, b, c=None): |
| ... pass |
| ... |
| >>> function = mocksignature(function) |
| >>> function.return_value = None |
| >>> function(1, 2) |
| >>> function.assert_called_with(1, 2, None) |
| |
| |
| Mocking methods and self |
| ~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| When you use `mocksignature` to replace a method on a class then `self` |
| will be included in the method signature - and you will need to include |
| the instance when you do your asserts. |
| |
| As a curious factor of the way Python (2) wraps methods fetched from a class, |
| we can *get* the `return_value` from a function set on a class, but we can't |
| set it. We have to do this through the exposed `mock` attribute instead: |
| |
| .. doctest:: |
| |
| >>> class SomeClass(object): |
| ... def method(self, a, b, c=None): |
| ... pass |
| ... |
| >>> SomeClass.method = mocksignature(SomeClass.method) |
| >>> SomeClass.method.mock.return_value = None |
| >>> instance = SomeClass() |
| >>> instance.method() |
| Traceback (most recent call last): |
| ... |
| TypeError: <lambda>() takes at least 4 arguments (1 given) |
| >>> instance.method(1, 2, 3) |
| >>> instance.method.assert_called_with(instance, 1, 2, 3) |
| |
| When you use `mocksignature` on instance methods `self` isn't included (and we |
| can set the `return_value` etc directly): |
| |
| .. doctest:: |
| |
| >>> class SomeClass(object): |
| ... def method(self, a, b, c=None): |
| ... pass |
| ... |
| >>> instance = SomeClass() |
| >>> instance.method = mocksignature(instance.method) |
| >>> instance.method.return_value = None |
| >>> instance.method(1, 2, 3) |
| >>> instance.method.assert_called_with(1, 2, 3) |
| |
| |
| mocksignature with classes |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| When used with a class `mocksignature` copies the signature of the `__init__` |
| method. |
| |
| .. doctest:: |
| |
| >>> class Something(object): |
| ... def __init__(self, foo, bar): |
| ... pass |
| ... |
| >>> MockSomething = mocksignature(Something) |
| >>> instance = MockSomething(10, 9) |
| >>> assert instance is MockSomething.return_value |
| >>> MockSomething.assert_called_with(10, 9) |
| >>> MockSomething() |
| Traceback (most recent call last): |
| ... |
| TypeError: <lambda>() takes at least 2 arguments (0 given) |
| |
| Because the object returned by `mocksignature` is a function rather than a |
| `Mock` you lose the other capabilities of `Mock`, like dynamic attribute |
| creation. |
| |
| |
| mocksignature with callable objects |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| When used with a callable object `mocksignature` copies the signature of the |
| `__call__` method. |
| |
| .. doctest:: |
| |
| >>> class Something(object): |
| ... def __call__(self, spam, eggs): |
| ... pass |
| ... |
| >>> something = Something() |
| >>> mock_something = mocksignature(something) |
| >>> result = mock_something(10, 9) |
| >>> mock_something.assert_called_with(10, 9) |
| >>> mock_something() |
| Traceback (most recent call last): |
| ... |
| TypeError: <lambda>() takes at least 2 arguments (0 given) |
| |
| |
| mocksignature argument to patch |
| ------------------------------- |
| |
| `mocksignature` is available as a keyword argument to :func:`patch` or |
| :func:`patch.object`. It can be used with functions / methods / classes and |
| callable objects. |
| |
| .. doctest:: |
| |
| >>> class SomeClass(object): |
| ... def method(self, a, b, c=None): |
| ... pass |
| ... |
| >>> @patch.object(SomeClass, 'method', mocksignature=True) |
| ... def test(mock_method): |
| ... instance = SomeClass() |
| ... mock_method.return_value = None |
| ... instance.method(1, 2) |
| ... mock_method.assert_called_with(instance, 1, 2, None) |
| ... |
| >>> test() |