Reputation: 1386
I want to add requests
mock for python.
Any call to requests.get
in a class will call the unittest mock function instead.
I don't want to add a call such as requests.get
in the unittest itself, it may happen in many places in code.
Now, changes on code - I get:
requests.exceptions.ConnectionError: HTTPConnectionPool(host='myurl1', port=80): Max retries exceeded with url: /test.txt (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fbc5da52e00>: Failed to establish a new connection: [Errno -2] Name does not resolve'))
i.e:
in main class: my_class
import requests
from urllib import request, parse
...
mymock = None
class MyClass():
def myfunc1(self):
...
# requests.get can be callled randomaly
requests.get('http://myurl1/test.txt')
def myfunc2(self):
requests.get('http://myurl2/test.txt')
and the unittest:
from s3_uploader import *
import pytest
import mock
import requests
import requests_mock
class MyMockClass(MyClass):
@requests_mock.mock()
def mock_get_requests(self, m):
m.get('http://myurl1/test.txt')
# if I call here to the base class myfunc1.myfunc1 - there is no exception
# but I don't want to implement like this (call the main function whenever the mock is declared):
class TestClass(unittest.TestCase):
def setUp(self):
mymock.mock_get_requests()
def test_myfunc1(self)
super.myfunc1() # this raise exception - the mock for request.get is not injected to the request.get class
if __name__ == '__main__':
mymock = MyMockClass()
unittest.main()
I don't want to do something like the following (which works, but I need to do it for lot of functions, and http requests.get
is not the only mock).
class MyMockClass(MyClass):
def mock_get_requests(self, m):
m.get('http://myurl1/test.txt')
super.myfunc1()
class TestClass(unittest.TestCase):
@requests_mock.mock(self)
def test_myfunc1(self, m)
mymock.mock_get_requests()
if __name__ == '__main__':
mymock = MyMockClass()
unittest.main()
Also I tried using the monkeypatch
This also use the original requests.get
and not the mocked one.
Like this:
class MockResponse:
@staticmethod
def json():
return {"mock_key": "mock_response"}
# monkeypatched requests.get moved to a fixture
@pytest.fixture
def mock_response(monkeypatch):
"""Requests.get() mocked to return {'mock_key':'mock_response'}."""
def mock_get(*args, **kwargs):
return MockResponse()
logging.info('**** test mock response ****')
monkeypatch.setattr(requests, "get", mock_get)
and in test class:
@pytest.mark.usefixtures("mock_response")
class TestClass(unittest.TestCase):
...
def test_requests(self):
requests.get(http://myurl/test.txt') # this not using the mock
# mymock.myfunc1 doesn't work either.
What may be the alternative solution for this problem?
Thanks.
Upvotes: 0
Views: 309
Reputation: 2548
Try with the following test code where I didn't use pytest
:
import unittest
from unittest import mock
from my_class import MyClass
class TestMyClass(unittest.TestCase):
@mock.patch("my_class.requests")
def test_func1(self, mock_requests):
sut = MyClass()
sut.myfunc1()
mock_requests.get.assert_called_once_with('http://myurl1/test.txt')
@mock.patch("my_class.requests")
def test_func2(self, mock_requests):
sut = MyClass()
sut.myfunc2()
mock_requests.get.assert_called_once_with('http://myurl2/test.txt')
if __name__ == "__main__":
unittest.main()
I suppose that:
myfunc1
and myfunc2
call request.get()
with a precise URL;MyClass
is contained in the file my_class.py
(see the import from my_class import MyClass
).When tests call functions myfunc1
and myfunc2
, they don't call the real requests.get()
function but the 2 tests contain necessarily the instruction: @mock.patch("my_class.requests")
, so I don't know if this solution is suited to answer to your question.
Upvotes: 1