How to mock a Python class that has two imports?

How can I mock a Python class that has two imports without changing the code in any of the imported modules? Suppose I import a web utilities library that imports HTTPClient () - how can I write a unit test that mocks HTTPClient to return a value without changing the web_utils.py file? I want to use data manipulation in the DataHandler (rather than mock it), but I don't want the HTTPClient to actually connect to the network.

Is it possible? Given that Python has a monkey fix, this certainly seems to be the case. Or is there an alternative / better way? I still understand the mocking process, much less changing the import.

# someLib/web_utils.py
from abc.client import SomeHTTPClient # the class to replace

def get_client():
    tc = SomeHTTPClient(endpoint='url') # fails when I replace the class
    return tc

class DataHandler(object):
    def post_data(someURL, someData):
        newData = massage(someData)
        client = get_client()
        some_response = client.request(someURL, 'POST', newData)
        return some_response

# code/myCode.py
from someLib.web_utils import DataHandler

dh = DataHandler()
reply = dh.post_data(url, data)

# tests/myTests.py
from django.test.testcases import TestCase
from mock import Mock

class Mocking_Test(TestCase):
    def test_mock(self):
        from someLib import web_utils
        fakeClient = Mock()
        fakeClient.request = web_utils.SomeHTTPClient.request # just to see if it works
        web_utils.SomeHTTPClient = fakeClient
        dh = DataHandler()
        reply = dh.post_data(url='somewhere', data='stuff')

- ​​ get_client(). , @spicavigo - , , SomeHTTPClient. - ( " , " ). , , Mock(), , . , .

+5
3

myTest.py

from someLib import web_utils
web_utils.SomeHTTPClient = <YOUR MOCK CLASS>
+4

, , :

class Mocking_Test(TestCase):
    def test_mock(self):
        def return_response(a, b, c, *parms, **args):
            print "in request().return_response"
            class makeResponse(object):
                status = 200
                reason = "making stuff up"
            return makeResponse()

        fakeClient = Mock()
        fakeClient.return_value = return_response

        #with patch('abc.client.SomeHTTPClient') as MockClient: # not working.
        with patch('abc.client.SomeHTTPClient.request', new_callable=fakeClient):
            dh = DataHandler()
            reply = dh.post_data(url='somewhere', data='stuff')

, patch, . , request ( , , HTTP-).

+2

I know that this is not quite what you wanted, but after creating your mock class I would just do something like this to make the code cleaner (in the bash shell, otherwise, if you have 1 file, just find and replace)

find . -name "*.py" -type f -exec sed -i "s/<old_class>/<mock_class>/g" '{}' \;

This should replace all instances of your old class with a new class, and I personally believe that this will make much cleaner code. If you need only one file and not all .py files, just change -name to "file.py"

-1
source

All Articles