1

I apologize for the confusing title, I wasn't sure there was a better way to describe the problem. I have a python library with the following structure:

library/
  src/
    code/
      __init__.py
      baseclass.py
      helpers.py
      class1.py
    tests/
      __init__.py
      test_library.py

I am attempting to test a function in a class in baseclass.py. The function in the class in baseclass.py returns class objects from class1.py as shown below: baseclass.py:

from class1 import DeviceClass

class LibraryBuilder:
    def __init__(self, user, password):
......

    def get_devices(self):
        devlist = helpers.call_api('api_url', 'post', header, json)
        #Return a list of dictionaries
        for dev in devlist:
            if dev = "DeviceType":
               return DeviceClass(dev, self)

test_library.py

import pytest
from unittest.mock import patch, Mock
from library.baseclass import LibraryBuilder
import library
from library.class1 import DeviceClass

class TestDeviceList(object):
    @pytest.fixture()
    def api_mock(self, caplog):
        self.mock_api_call = patch('library.helpers.call_api', autospec=True)
        self.mock_api = self.mock_api_call.start()
        self.mock_api.return_value.ok = True
        self.library_obj = library.baseclass.LibraryBuilder('sam@mail.com', 'pass')
        yield
        self.mock_api_call.stop()

    @patch.object('baseclass.class1', 'DeviceClass', autospec=True)
    def test_get_devices_all(self, caplog, dev_mock, api_mock):
        self.mock_api.return_value = return_dict
        devs = self.library_object.get_devices()
        dev_mock.assert_called_with(return_dict, self.library_object)

The test fails because 'device_object' is never called. When I debug, I see that the device_patch object that is created isn't the mocked object, its the actual DeviceClass object.

I've tried to reference the device_object path to patch.object('library.baseclass', 'DeviceClass', autospec=True). I've tried variations of the imported classes because I believe this is related to the thread below but I can't figure out where I am going wrong: Why python mock patch doesn't work?

The call_api mock works as it should. The library_object returns the actual instantiated class based off of the call_api mocked return_value

I just refactored the code from a single file to this configuration and the test was passing before that. Any ideas on what I'm missing?

EDIT

I debugged even further and I believe it has to do with DeviceClass inheriting from DeviceBaseClass so that device_class.py looks like:

class DeviceBaseClass(object):
    def __init__(self, details, manager):
         self.details = {}
..............
class DeviceClass(DeviceBaseClass):
    def __init__(self, details, manager):
        super(DeviceClass, self).__init__(details, manager)

so now I am getting the message TypeError: super() argument 1 must be type not MagicMock. I'm guessing since I'm mocking DeviceClass the mocked class is being called in the super() method. I've seen a few other posts on this but haven't found a solution. Am I missing something obvious or am I on the completely wrong track?

Joe6.626070
  • 321
  • 1
  • 9

1 Answers1

1

Finally figured it out, as I thought it was the location of the imported module. I tried every variation possible and the solution is to make sure the object you are patching from where it is being called. I don't know why I didn't see this last night!!

The original call patch was @patch('baseclass.class1', 'DeviceClass', autospec=True) The correct patch is @patch('baseclass.DeviceClass', autospec=True) as shown below

import pytest
from unittest.mock import patch, Mock
from library.baseclass import LibraryBuilder
import library
from library.class1 import DeviceClass

class TestDeviceList(object):
    @pytest.fixture()
    def api_mock(self, caplog):
        self.mock_api_call = patch('library.helpers.call_api', autospec=True)
        self.mock_api = self.mock_api_call.start()
        self.mock_api.return_value.ok = True
        self.library_obj = library.baseclass.LibraryBuilder('sam@mail.com', 'pass')
        yield
        self.mock_api_call.stop()

    @patch('Library.baseclass.DeviceClass', autospec=True)
    def test_get_devices_all(self, caplog, dev_mock, api_mock):
        self.mock_api.return_value = return_dict
        devs = self.library_object.get_devices()
        dev_mock.assert_called_with(return_dict, self.library_object)
Joe6.626070
  • 321
  • 1
  • 9