Allow to decorate class with mock
Now it is possible to mock not only a function but also a class: class TestClass(object) def test_func_a(self, m): m.register_uri('GET', 'http://test.com', text='data') ... def test_func_b(self, m) m.register_uri('GET', 'http://test.com', text='data') ... This new behavior mimics behavior of `patch` from `mock` library. Added docs for this new feature. Closes-Bug: #1404805 Change-Id: I8303dc4bc682cf12ffe86e7712b5a1c54de83efb
This commit is contained in:
parent
931f2b4989
commit
1774bb11a9
@ -47,6 +47,37 @@ If the position of the mock is likely to conflict with other arguments you can p
|
||||
>>> test_kw_function()
|
||||
'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
|
||||
=======
|
||||
|
||||
|
@ -123,6 +123,10 @@ class MockerCore(object):
|
||||
class Mocker(MockerCore):
|
||||
"""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):
|
||||
"""Create a new mocker adapter.
|
||||
|
||||
@ -141,7 +145,26 @@ class Mocker(MockerCore):
|
||||
def __exit__(self, type, value, traceback):
|
||||
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)
|
||||
def inner(*args, **kwargs):
|
||||
with self as m:
|
||||
@ -155,5 +178,25 @@ class Mocker(MockerCore):
|
||||
|
||||
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
|
||||
|
@ -88,6 +88,70 @@ class MockerTests(base.TestCase):
|
||||
inner()
|
||||
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()
|
||||
def test_query_string(self, m):
|
||||
url = 'http://test.url/path'
|
||||
@ -114,6 +178,13 @@ class MockerTests(base.TestCase):
|
||||
self.assertEqual(m.request_history, matcher.request_history)
|
||||
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):
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user