mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-11-19 02:13:04 +00:00
263 lines
7.9 KiB
Plaintext
263 lines
7.9 KiB
Plaintext
|
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()
|