Невозможно исправить магический атрибут - PullRequest
0 голосов
/ 07 октября 2019

Я бы хотел иметь возможность исправить магический атрибут.

Основной файл, который я хотел бы проверить, это 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 вместо ошибки атрибута.

Ответы [ 2 ]

1 голос
/ 07 октября 2019

С patch() важно исправлять объекты в пространстве имен, в котором они находятся. См. , где патчи в документах.

В вашем случае это потому, что строка

return __file__

не разрешает имя __file__ при поиске атрибутав случае. Он разрешается в пространстве имен модуля.

Следовательно, вам нужно будет исправить его в правильном месте (т. Е. На модуле, в котором определен FileUtil).

0 голосов
/ 17 октября 2019

Я разбил оператор __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()) 
...