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