首页 > 解决方案 > 无法修补魔法属性

问题描述

我希望能够修补魔法属性。

我要测试的主文件是 FileUtil.py,其中包含 FileUtil 的类定义:

class FileUtil:
    @logit(showArgs=True, showRetVal=True)
    def exec_file_path(self) -> str:
        """
        Get the full dir and file path we're executing from using __file__, or if not available (probably because you're using a Jupyter notebook)
        use os.path.abspath.
        """
        try:
            logger.debug('exec_file_path is about to attempt accessing __file__.')
            return __file__
        except NameError as err:
            logger.debug('Exception message: {msg}'.format(msg=err))
            return abspath('')

我在 mockTest.py 中也有一个本地测试,其中包含一个 AltFileUtil 类。为了区分调用的是哪一个,fUtil 实例化了 AltFileUtil,而 gUtil 实例化了 FileUtil。

被测方法使用__file__,但除此之外,使用absdir.

from os.path import abspath
from unittest.mock import MagicMock, PropertyMock, patch

class AltFileUtil:
    def exec_file_path(self) -> str:
        """
        Get the full dir and file path we're executing from using __file__, or if not available (probably because you're using a Jupyter notebook)
        use os.path.abspath.
        """
        try:
            return __file__
        except NameError as err:
            return abspath('')

fUtil = AltFileUtil()
print (f'Part 1. function returns: {fUtil.exec_file_path()}')

patches = ['__main__.AltFileUtil', 'mockTest.AltFileUtil', ]
for p in patches:
    print (f'Using patch of {p}.')
    with patch(p) as mockFUtil:
        type(mockFUtil.return_value).__file__ = PropertyMock(return_value='mockfilename')
        x = mockFUtil.exec_file_path()
        try:
            print (f'Success! Got {x.__file__}.')
        except (ValueError, AttributeError, TypeError) as e:
            print(f'Got a {type(e)} error: {e}')

from FileUtil import FileUtil
gUtil = FileUtil()
print (f'Part 2. Using function from FileUtil.py, which returns: {gUtil.exec_file_path()}')

patches = ['FileUtil.FileUtil',  ]
for p in patches:
    print (f'Using patch of {p}.')
    with patch(p) as mockFUtil:
        type(mockFUtil.return_value).__file__ = PropertyMock(return_value='mockfilename')
        x = mockFUtil.exec_file_path()
        try:
            print (f'Success! Got {x.__file__}.')
        except (ValueError, AttributeError, TypeError) as e:
            print(f'Got a {type(e)} error: {e}')

输出是

C:\Users\Owner\PycharmProjects\Utilities\venv\Scripts\python.exe C:/Users/Owner/.PyCharm2019.2/config/scratches/mockTest.py
Part 1. function returns: C:/Users/Owner/.PyCharm2019.2/config/scratches/mockTest.py
Using patch of __main__.AltFileUtil.
Got a <class 'AttributeError'> error: __file__
Using patch of mockTest.AltFileUtil.
Part 1. function returns: C:\Users\Owner\.PyCharm2019.2\config\scratches\mockTest.py
Using patch of __main__.AltFileUtil.
Got a <class 'AttributeError'> error: __file__
Using patch of mockTest.AltFileUtil.
Got a <class 'AttributeError'> error: __file__
Part 2. Using function from FileUtil.py, which returns: C:\Users\Owner\PycharmProjects\Utilities\FileUtil.py
Using patch of FileUtil.FileUtil.
Got a <class 'AttributeError'> error: __file__
Got a <class 'AttributeError'> error: __file__
Part 2. Using function from FileUtil.py, which returns: C:\Users\Owner\PycharmProjects\Utilities\FileUtil.py
Using patch of FileUtil.FileUtil.
2019-10-08 07:42:13,797 DEBUG Entering exec_file_path.
Got a <class 'AttributeError'> error: __file__
2019-10-08 07:42:13,797 DEBUG >> 0. <FileUtil.FileUtil object at 0x03721230>
2019-10-08 07:42:13,797 DEBUG exec_file_path is about to attempt accessing __file__.
2019-10-08 07:42:13,797 DEBUG >> Return value is C:\Users\Owner\PycharmProjects\Utilities\FileUtil.py.
2019-10-08 07:42:13,797 DEBUG Exiting exec_file_path.
2019-10-08 07:42:13,797 DEBUG Entering exec_file_path.
2019-10-08 07:42:13,797 DEBUG >> 0. <FileUtil.FileUtil object at 0x02E64050>
2019-10-08 07:42:13,797 DEBUG exec_file_path is about to attempt accessing __file__.
2019-10-08 07:42:13,797 DEBUG >> Return value is C:\Users\Owner\PycharmProjects\Utilities\FileUtil.py.
2019-10-08 07:42:13,797 DEBUG Exiting exec_file_path.

Process finished with exit code 0

我认为它应该给我mockfilename而不是属性错误。

标签: pythonunit-testingpython-mock

解决方案


patch()查找对象的名称空间中修补对象很重要。查看文档中的补丁位置。

在您的情况下,这是因为该行

return __file__

__file__通过在实例上查找属性来解析名称。它在模块命名空间中解析。

因此,您需要将其修补到正确的位置(即在FileUtil定义的模块上)。


推荐阅读