Eitan
Eitan

Reputation: 1386

python - add mock for requests.get without need to explicitly check it

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

Answers (1)

User051209
User051209

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:

  1. for you is enough to test that myfunc1 and myfunc2 call request.get() with a precise URL;
  2. the class 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

Related Questions