129 lines
4.1 KiB
Python
129 lines
4.1 KiB
Python
import sys
|
|
import inspect
|
|
|
|
import pytest
|
|
|
|
|
|
if sys.version_info >= (3, 3): # pragma: no cover
|
|
import unittest.mock as mock_module
|
|
else:
|
|
import mock as mock_module
|
|
|
|
|
|
class MockFixture(object):
|
|
"""
|
|
Fixture that provides the same interface to functions in the mock module,
|
|
ensuring that they are uninstalled at the end of each test.
|
|
"""
|
|
|
|
Mock = mock_module.Mock
|
|
MagicMock = mock_module.MagicMock
|
|
ANY = mock_module.ANY
|
|
|
|
def __init__(self):
|
|
self._patches = [] # list of mock._patch objects
|
|
self.patch = self._Patcher(self._patches)
|
|
|
|
def stopall(self):
|
|
"""
|
|
Stop all patchers started by this fixture. Can be safely called multiple
|
|
times.
|
|
"""
|
|
for p in reversed(self._patches):
|
|
p.stop()
|
|
self._patches[:] = []
|
|
|
|
def spy(self, obj, name):
|
|
"""
|
|
Creates a spy of method. It will run method normally, but it is now
|
|
possible to use `mock` call features with it, like call count.
|
|
|
|
:param object obj: An object.
|
|
:param unicode name: A method in object.
|
|
:rtype: mock.MagicMock
|
|
:return: Spy object.
|
|
"""
|
|
method = getattr(obj, name)
|
|
|
|
autospec = inspect.ismethod(method) or inspect.isfunction(method)
|
|
# Can't use autospec classmethod or staticmethod objects
|
|
# see: https://bugs.python.org/issue23078
|
|
if inspect.isclass(obj):
|
|
# bypass class descriptor:
|
|
# http://stackoverflow.com/questions/14187973/python3-check-if-method-is-static
|
|
value = obj.__getattribute__(obj, name)
|
|
if isinstance(value, (classmethod, staticmethod)):
|
|
autospec = False
|
|
|
|
result = self.patch.object(obj, name, side_effect=method,
|
|
autospec=autospec)
|
|
return result
|
|
|
|
def stub(self):
|
|
"""
|
|
Creates a stub method. It accepts any arguments. Ideal to register to
|
|
callbacks in tests.
|
|
|
|
:rtype: mock.MagicMock
|
|
:return: Stub object.
|
|
"""
|
|
return mock_module.MagicMock(spec=lambda *args, **kwargs: None)
|
|
|
|
class _Patcher(object):
|
|
"""
|
|
Object to provide the same interface as mock.patch, mock.patch.object,
|
|
etc. We need this indirection to keep the same API of the mock package.
|
|
"""
|
|
|
|
def __init__(self, patches):
|
|
self._patches = patches
|
|
|
|
def _start_patch(self, mock_func, *args, **kwargs):
|
|
"""Patches something by calling the given function from the mock
|
|
module, registering the patch to stop it later and returns the
|
|
mock object resulting from the mock call.
|
|
"""
|
|
p = mock_func(*args, **kwargs)
|
|
mocked = p.start()
|
|
self._patches.append(p)
|
|
return mocked
|
|
|
|
def object(self, *args, **kwargs):
|
|
"""API to mock.patch.object"""
|
|
return self._start_patch(mock_module.patch.object, *args, **kwargs)
|
|
|
|
def multiple(self, *args, **kwargs):
|
|
"""API to mock.patch.multiple"""
|
|
return self._start_patch(mock_module.patch.multiple, *args,
|
|
**kwargs)
|
|
|
|
def dict(self, *args, **kwargs):
|
|
"""API to mock.patch.dict"""
|
|
return self._start_patch(mock_module.patch.dict, *args, **kwargs)
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
"""API to mock.patch"""
|
|
return self._start_patch(mock_module.patch, *args, **kwargs)
|
|
|
|
|
|
@pytest.yield_fixture
|
|
def mocker():
|
|
"""
|
|
return an object that has the same interface to the `mock` module, but
|
|
takes care of automatically undoing all patches after each test method.
|
|
"""
|
|
result = MockFixture()
|
|
yield result
|
|
result.stopall()
|
|
|
|
|
|
@pytest.fixture
|
|
def mock(mocker):
|
|
"""
|
|
Same as "mocker", but kept only for backward compatibility.
|
|
"""
|
|
import warnings
|
|
warnings.warn('"mock" fixture has been deprecated, use "mocker" instead',
|
|
DeprecationWarning)
|
|
return mocker
|