无法修补魔法属性
Cannot patch a magic attribute
我希望能够修补魔法属性。
我想测试的主文件是 FileUtil.py,包含 FileUtil 的 class 定义:
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 中也有一个本地测试,其中包含一个 class 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
而不是属性错误。
对于 patch()
,重要的是在查找它们的名称空间中修补对象。请参阅文档中的 where to patch。
在你的情况下,这是因为行
return __file__
不通过查找实例的属性来解析名称 __file__
。它在模块命名空间中解析。
因此,您需要在正确的位置(即在定义了 FileUtil
的模块上)对其进行修补。
我已将 __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('')
以下是我如何修补测试并更改 return 值。
@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())
我希望能够修补魔法属性。
我想测试的主文件是 FileUtil.py,包含 FileUtil 的 class 定义:
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 中也有一个本地测试,其中包含一个 class 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
而不是属性错误。
对于 patch()
,重要的是在查找它们的名称空间中修补对象。请参阅文档中的 where to patch。
在你的情况下,这是因为行
return __file__
不通过查找实例的属性来解析名称 __file__
。它在模块命名空间中解析。
因此,您需要在正确的位置(即在定义了 FileUtil
的模块上)对其进行修补。
我已将 __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('')
以下是我如何修补测试并更改 return 值。
@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())