Reputation: 14145
I have a situation that is similar to this:
import unittest
import requests_mock
import requests
import mock
class ClassUnderTest:
def __init__(self):
self.session = requests.Session()
self.foo_url = 'http://google.com'
def do_foo(self):
return self.session.get(self.foo_url)
def do_foo_failing(self):
# I want to prevent accidentally doing this sort of operation
# instead of reusuing self.session
return requests.get(self.foo_url)
I want to make sure that the methods perform correctly, but also ensure I am using the self.session
object. In practice I have encountered this issue because I errantly added a requests.get
call instead of re-usuing the same session.
However this means tests like this do not actually test this functionality:
class TestClassUnderTest(unittest.TestCase):
@requests_mock.mock()
def test_do_foo_should_pass(self, m):
c = ClassUnderTest()
m.get('http://google.com', status_code=200)
r = c.do_foo()
self.assertEqual(200, r.status_code)
self.assertEqual(m.call_count, 1)
@requests_mock.mock()
def test_do_foo_should_fail(self, m):
c = ClassUnderTest()
m.get('http://google.com', status_code=200)
r = c.do_foo_failing()
self.assertEqual(200, r.status_code)
self.assertEqual(m.call_count, 1)
I have thought about replacing self.session
with a mock, but then I also have to do things like set status_code on the mock (and in my real code example I also need to do things like add a mock .json()
method so that the code consuming the response functions correctly).
Is there a way using requests_mock to effectively guarantee that only self.session
is being used (and not requests.get
in this example)?
Upvotes: 1
Views: 568
Reputation: 14145
The following works by wrapping all of the request.Session
functions to a MockClass, with a wrapper that tracks usage of them:
class MockSession:
def __init__(self):
self.session = requests.Session()
self._called_methods = {}
def session_decorator(f):
def func(*args, **kwargs):
if f.__name__ not in self._called_methods:
self._called_methods[f.__name__] = 1
else:
self._called_methods[f.__name__] += 1
return f(*args, **kwargs)
return func
for name, f in inspect.getmembers(self.session):
if inspect.ismethod(f):
setattr(self, name, session_decorator(f))
After using this mock you can access the ._called_methods
value to check how often individual methods are called. Note because of how requests_mock works you must do this at runtime (rather than loadtime by simply extending the requests.Session class with similar functionality).
Modifying the test code results in:
class TestClassUnderTest(unittest.TestCase):
@requests_mock.mock()
def test_do_foo_should_pass(self, m):
c = ClassUnderTest()
c.session = MockSession()
m.get('http://google.com', status_code=200)
r = c.do_foo()
self.assertEqual(200, r.status_code)
self.assertEqual(m.call_count, 1)
self.assertEqual(c.session._called_count['GET'], 1)
@requests_mock.mock()
def test_do_foo_should_fail(self, m):
c = ClassUnderTest()
c.session = MockSession()
m.get('http://google.com', status_code=200)
r = c.do_foo_failing()
self.assertEqual(200, r.status_code)
self.assertEqual(m.call_count, 1)
# This will fail with an attribute error
# because that function was never invoked on the mock session
self.assertEqual(c.session._called_count['GET'], 1)
Upvotes: 1