Merge "Allow to decorate class with mock"
This commit is contained in:
@@ -47,6 +47,37 @@ If the position of the mock is likely to conflict with other arguments you can p
|
|||||||
>>> test_kw_function()
|
>>> test_kw_function()
|
||||||
'resp'
|
'resp'
|
||||||
|
|
||||||
|
Class Decorator
|
||||||
|
===============
|
||||||
|
|
||||||
|
Mocker can also be used to decorate a whole class. It works exactly like in case of decorating a normal function.
|
||||||
|
When used in this way they wrap every test method on the class. The mocker recognise methods that start with *test* as being test methods.
|
||||||
|
This is the same way that the `unittest.TestLoader` finds test methods by default.
|
||||||
|
It is possible that you want to use a different prefix for your tests. You can inform the mocker of the different prefix by setting `requests_mock.Mocker.TEST_PREFIX`:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> requests_mock.Mocker.TEST_PREFIX = 'foo'
|
||||||
|
>>>
|
||||||
|
>>> @requests_mock.Mocker()
|
||||||
|
... class Thing(object):
|
||||||
|
... def foo_one(self, m):
|
||||||
|
... m.register_uri('GET', 'http://test.com', text='resp')
|
||||||
|
... return requests.get('http://test.com').text
|
||||||
|
... def foo_two(self, m):
|
||||||
|
... m.register_uri('GET', 'http://test.com', text='resp')
|
||||||
|
... return requests.get('http://test.com').text
|
||||||
|
...
|
||||||
|
>>>
|
||||||
|
>>> Thing().foo_one()
|
||||||
|
'resp'
|
||||||
|
>>> Thing().foo_two()
|
||||||
|
'resp'
|
||||||
|
|
||||||
|
|
||||||
|
This behavior mimics how patchers from `mock` library works.
|
||||||
|
|
||||||
|
|
||||||
Methods
|
Methods
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,10 @@ class MockerCore(object):
|
|||||||
class Mocker(MockerCore):
|
class Mocker(MockerCore):
|
||||||
"""The standard entry point for mock Adapter loading.
|
"""The standard entry point for mock Adapter loading.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
#: Defines with what should method name begin to be patched
|
||||||
|
TEST_PREFIX = 'test'
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
"""Create a new mocker adapter.
|
"""Create a new mocker adapter.
|
||||||
|
|
||||||
@@ -141,7 +145,26 @@ class Mocker(MockerCore):
|
|||||||
def __exit__(self, type, value, traceback):
|
def __exit__(self, type, value, traceback):
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
def __call__(self, func):
|
def __call__(self, obj):
|
||||||
|
if isinstance(obj, type):
|
||||||
|
return self.decorate_class(obj)
|
||||||
|
|
||||||
|
return self.decorate_callable(obj)
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
"""Returns an exact copy of current mock
|
||||||
|
"""
|
||||||
|
m = Mocker(
|
||||||
|
kw=self._kw,
|
||||||
|
real_http=self._real_http
|
||||||
|
)
|
||||||
|
return m
|
||||||
|
|
||||||
|
def decorate_callable(self, func):
|
||||||
|
"""Decorates a callable
|
||||||
|
|
||||||
|
:param callable func: callable to decorate
|
||||||
|
"""
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def inner(*args, **kwargs):
|
def inner(*args, **kwargs):
|
||||||
with self as m:
|
with self as m:
|
||||||
@@ -155,5 +178,25 @@ class Mocker(MockerCore):
|
|||||||
|
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
def decorate_class(self, klass):
|
||||||
|
"""Decorates methods in a class with request_mock
|
||||||
|
|
||||||
|
Method will be decorated only if it name begins with `TEST_PREFIX`
|
||||||
|
|
||||||
|
:param object klass: class which methods will be decorated
|
||||||
|
"""
|
||||||
|
for attr_name in dir(klass):
|
||||||
|
if not attr_name.startswith(self.TEST_PREFIX):
|
||||||
|
continue
|
||||||
|
|
||||||
|
attr = getattr(klass, attr_name)
|
||||||
|
if not hasattr(attr, '__call__'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
m = self.copy()
|
||||||
|
setattr(klass, attr_name, m(attr))
|
||||||
|
|
||||||
|
return klass
|
||||||
|
|
||||||
|
|
||||||
mock = Mocker
|
mock = Mocker
|
||||||
|
|||||||
@@ -88,6 +88,70 @@ class MockerTests(base.TestCase):
|
|||||||
inner()
|
inner()
|
||||||
self.assertMockStopped()
|
self.assertMockStopped()
|
||||||
|
|
||||||
|
def test_with_class_decorator(self):
|
||||||
|
outer = self
|
||||||
|
|
||||||
|
@requests_mock.mock()
|
||||||
|
class Decorated(object):
|
||||||
|
|
||||||
|
def test_will_be_decorated(self, m):
|
||||||
|
outer.assertMockStarted()
|
||||||
|
outer._do_test(m)
|
||||||
|
|
||||||
|
def will_not_be_decorated(self):
|
||||||
|
outer.assertMockStopped()
|
||||||
|
|
||||||
|
decorated_class = Decorated()
|
||||||
|
|
||||||
|
self.assertMockStopped()
|
||||||
|
decorated_class.test_will_be_decorated()
|
||||||
|
self.assertMockStopped()
|
||||||
|
decorated_class.will_not_be_decorated()
|
||||||
|
self.assertMockStopped()
|
||||||
|
|
||||||
|
def test_with_class_decorator_and_custom_kw(self):
|
||||||
|
outer = self
|
||||||
|
|
||||||
|
@requests_mock.mock(kw='custom_m')
|
||||||
|
class Decorated(object):
|
||||||
|
|
||||||
|
def test_will_be_decorated(self, **kwargs):
|
||||||
|
outer.assertMockStarted()
|
||||||
|
outer._do_test(kwargs['custom_m'])
|
||||||
|
|
||||||
|
def will_not_be_decorated(self):
|
||||||
|
outer.assertMockStopped()
|
||||||
|
|
||||||
|
decorated_class = Decorated()
|
||||||
|
|
||||||
|
self.assertMockStopped()
|
||||||
|
decorated_class.test_will_be_decorated()
|
||||||
|
self.assertMockStopped()
|
||||||
|
decorated_class.will_not_be_decorated()
|
||||||
|
self.assertMockStopped()
|
||||||
|
|
||||||
|
@mock.patch.object(requests_mock.mock, 'TEST_PREFIX', 'foo')
|
||||||
|
def test_with_class_decorator_and_custom_test_prefix(self):
|
||||||
|
outer = self
|
||||||
|
|
||||||
|
@requests_mock.mock()
|
||||||
|
class Decorated(object):
|
||||||
|
|
||||||
|
def foo_will_be_decorated(self, m):
|
||||||
|
outer.assertMockStarted()
|
||||||
|
outer._do_test(m)
|
||||||
|
|
||||||
|
def will_not_be_decorated(self):
|
||||||
|
outer.assertMockStopped()
|
||||||
|
|
||||||
|
decorated_class = Decorated()
|
||||||
|
|
||||||
|
self.assertMockStopped()
|
||||||
|
decorated_class.foo_will_be_decorated()
|
||||||
|
self.assertMockStopped()
|
||||||
|
decorated_class.will_not_be_decorated()
|
||||||
|
self.assertMockStopped()
|
||||||
|
|
||||||
@requests_mock.mock()
|
@requests_mock.mock()
|
||||||
def test_query_string(self, m):
|
def test_query_string(self, m):
|
||||||
url = 'http://test.url/path'
|
url = 'http://test.url/path'
|
||||||
@@ -114,6 +178,13 @@ class MockerTests(base.TestCase):
|
|||||||
self.assertEqual(m.request_history, matcher.request_history)
|
self.assertEqual(m.request_history, matcher.request_history)
|
||||||
self.assertIs(m.last_request, matcher.last_request)
|
self.assertIs(m.last_request, matcher.last_request)
|
||||||
|
|
||||||
|
def test_copy(self):
|
||||||
|
mocker = requests_mock.mock(kw='foo', real_http=True)
|
||||||
|
copy_of_mocker = mocker.copy()
|
||||||
|
self.assertIsNot(copy_of_mocker, mocker)
|
||||||
|
self.assertEqual(copy_of_mocker._kw, mocker._kw)
|
||||||
|
self.assertEqual(copy_of_mocker._real_http, mocker._real_http)
|
||||||
|
|
||||||
|
|
||||||
class MockerHttpMethodsTests(base.TestCase):
|
class MockerHttpMethodsTests(base.TestCase):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user