【问题标题】:Cannot patch a magic attribute无法修补魔法属性
【发布时间】:2019-10-07 19:43:01
【问题描述】:

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

我要测试的主文件是 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而不是属性错误。

【问题讨论】:

    标签: python unit-testing python-mock


    【解决方案1】:

    对于patch(),在查找对象的命名空间中修补对象非常重要。请参阅文档中的 where to patch

    在你的情况下,这是因为行

    return __file__
    

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

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

    【讨论】:

    • 我已经修改了 Python 脚本以更准确地反映我要测试的内容。我已经尝试了本地文件中的 ['main.AltFileUtil', 'mockTest.AltFileUtil', ] 和外部实用程序文件中的 'FileUtil.FileUtil' 的补丁。虽然我认为其中一个是修补的正确位置,但我仍然收到 AttributeErrors。
    【解决方案2】:

    我已将 __file__ 语句分解为一个单独的、可修补的成员。

    class FileUtil:
        def executing_file(self):
            return globals()['__file__']
    
        def exec_file_path(self) -> str:
            try:
                ex_file = self.executing_file()
                return ex_file
            except NameError as err:
                logger.debug('Exception message: {msg}'.format(msg=err))
                return abspath('')
    

    以下是我如何修补测试并更改返回值。

    @mock.patch.object(FileUtil, 'executing_file')
    def test_exec_file_path(self, mock_obj):
        fUtil = FileUtil()
        mock_obj.return_value = r'C:\mockpath\mockfile.py'
        self.assertEqual(r'C:\mockpath\mockfile.py', fUtil.exec_file_path())
    

    下面是如何修补它,使其抛出NameError(并将使用修补后的abspath)。

    @mock.patch('FileUtil.abspath')
    @mock.patch.object(FileUtil, 'executing_file')
    def test_exec_file_path_err(self, mock_obj, mock_abs):
        fUtil = FileUtil()
        mock_abs.return_value = r'c:\abspath\mockfile.py'
        mock_obj.side_effect = NameError(mock_obj, 'mock name error')
        self.assertEqual(r'c:\abspath\mockfile.py', fUtil.exec_file_path()) 
    

    【讨论】:

      猜你喜欢
      • 2017-04-30
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-27
      • 1970-01-01
      • 2015-11-10
      • 1970-01-01
      相关资源
      最近更新 更多