Skip to content

Instantly share code, notes, and snippets.

@pelme
Created July 22, 2013 16:01
Show Gist options
  • Save pelme/59a1dee00b5f8afc278e to your computer and use it in GitHub Desktop.
Save pelme/59a1dee00b5f8afc278e to your computer and use it in GitHub Desktop.
stub fixture - use at your own risk
from mock import patch, DEFAULT
def is_exception(thing):
try:
return isinstance(thing, BaseException) or issubclass(thing, BaseException)
except TypeError:
return False
def _patch_method(obj, method_name, return_value=DEFAULT, **kwargs):
if is_exception(return_value):
kwargs['side_effect'] = return_value
else:
kwargs['return_value'] = return_value
kwargs['autospec'] = True
# Allow specifying a dotted string instead of an object
if isinstance(obj, basestring):
return patch('%s.%s' % (obj, method_name), **kwargs)
assert callable(getattr(obj, method_name)), 'Tried to mock %s.%s which is not callable!' % (obj, method_name)
return patch.object(obj, method_name, **kwargs)
class Mocks(object):
def __init__(self, data):
self.__dict__.update(data)
class Stub(object):
def __init__(self):
self.started_patchers = []
self.mocks = []
def _patch(self, target, checked, **kwargs):
patchers = dict((k, _patch_method(target, k, v)) for k, v in kwargs.items())
mocks = {}
try:
# Keep a list of the patchers that has had .start() *successfully* called
for k, patcher in patchers.items():
# Might raise exception on non-existent method
mocks[k] = patcher.start()
self.started_patchers.append(patcher)
# All patchers could be activated
self.mocks.append((checked, mocks.values()))
return Mocks(mocks)
except Exception:
self.undo(avoid_call_checks=True)
raise
def returns(self, target, **kwargs):
return self._patch(target, checked=True, **kwargs)
def returns_nocheck(self, target, **kwargs):
return self._patch(target, checked=False, **kwargs)
def raises(self, target, **kwargs):
return self._patch(target, checked=True, **kwargs)
def raises_nocheck(self, target, **kwargs):
return self._patch(target, checked=False, **kwargs)
def undo(self, avoid_call_checks=False):
for patcher in self.started_patchers:
patcher.stop()
if avoid_call_checks:
return
for checked, mocks in self.mocks:
if checked:
for mock in mocks:
assert mock.mock_calls, '%s has no calls!' % repr(mock)
@pytest.fixture
def stub(request):
s = Stub()
request.addfinalizer(s.undo)
return s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment