首页 > 解决方案 > 修补从模拟类调用的类函数

问题描述

对于令人困惑的标题,我深表歉意,我不确定是否有更好的方法来描述问题。我有一个具有以下结构的python库:

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

我正在尝试在 baseclass.py 的一个类中测试一个函数。baseclass.py 中类中的函数从class1.py 中返回类对象,如下所示: 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)

测试失败,因为从未调用过“device_object”。当我调试时,我看到创建的 device_patch 对象不是模拟对象,它是实际的 DeviceClass 对象。

我试图将 device_object 路径引用到patch.object('library.baseclass', 'DeviceClass', autospec=True). 我尝试了导入类的变体,因为我相信这与下面的线程有关,但我不知道我哪里出错了: 为什么 python 模拟补丁不起作用?

call_api 模拟可以正常工作。library_object 返回基于 call_api 模拟的 return_value 的实际实例化类

我只是将代码从单个文件重构为这个配置,并且在此之前测试通过了。关于我所缺少的任何想法?

编辑

我进一步调试,我相信它与DeviceClass继承有关,DeviceBaseClass因此 device_class.py 看起来像:

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

所以现在我收到了消息TypeError: super() argument 1 must be type not MagicMock。我猜,因为我在模拟 DeviceClass,所以在 super() 方法中调用了模拟类。我已经看过其他一些关于此的帖子,但还没有找到解决方案。我是否遗漏了一些明显的东西,还是我走错了路?

标签: pythonunit-testingmockingpython-unittest

解决方案


终于弄明白了,因为我以为是导入模块的位置。我尝试了所有可能的变化,解决方案是确保你正在修补的对象是从它被调用的地方开始的。我不知道为什么我昨晚没看到这个!!

原来调用patch是@patch('baseclass.class1', 'DeviceClass', autospec=True)正确的patch@patch('baseclass.DeviceClass', autospec=True)如下图

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)

推荐阅读