如何模拟在同名模块内的函数中调用的函数?
How to mock a function called in a function inside a module with the same name?
我正在尝试使用 unittest.mock
,但出现错误:
AttributeError: does not have the attribute 'get_pledge_frequency'
我有以下文件结构:
pledges/views/
├── __init__.py
├── util.py
└── user_profile.py
pledges/tests/unit/profile
├── __init__.py
└── test_user.py
里面 pledges/views/__init___.py
我有:
from .views import *
from .account import account
from .splash import splash
from .preferences import preferences
from .user_profile import user_profile
在内部,user_profile.py
我有一个名为 user_profile
的函数,它在 util.py
内部调用一个名为 get_pledge_frequency
的函数,如下所示:
def user_profile(request, user_id):
# some logic
# !!!!!!!!!!!!!!!!
a, b = get_pledge_frequency(parameter) # this is the function I want to mock
# more logic
return some_value
我在test_user.py
里面测试如下:
def test_name():
with mock.patch(
"pledges.views.user_profile.get_pledge_frequency"
) as get_pledge_frequency:
get_pledge_frequency.return_value = ([], [])
response = c.get(
reverse("pledges:user_profile", kwargs={"user_id": user.id})
) # this calls the function user_profile inside pledges.user_profile
# some asserts to verify functionality
我查了其他问题,但是当有一个函数称为模块,并且在__init__
文件中导入时,答案不包括。
那么,有什么办法可以解决这个问题吗?我基本上已将文件 user_profile.py
重命名为 profile
然后我更改了测试以引用此模块内的函数,但我想知道是否可以保持函数和模块相同名字.
事实证明,可以模拟在同名模块内的函数中调用的函数。
unittest.mock.patch()
周围的小包装器可以像这样实现:
代码:
from unittest import mock
import importlib
def module_patch(*args):
target = args[0]
components = target.split('.')
for i in range(len(components), 0, -1):
try:
# attempt to import the module
imported = importlib.import_module('.'.join(components[:i]))
# module was imported, let's use it in the patch
patch = mock.patch(*args)
patch.getter = lambda: imported
patch.attribute = '.'.join(components[i:])
return patch
except Exception as exc:
pass
# did not find a module, just return the default mock
return mock.patch(*args)
使用:
而不是:
mock.patch("module.a.b")
你需要:
module_patch("module.a.b")
这是如何工作的?
基本思路是尝试从最长的可能模块路径开始向最短路径导入模块,如果导入成功,则使用该模块作为修补对象。
测试代码:
import module
print('module.a(): ', module.a())
print('module.b(): ', module.b())
print('--')
with module_patch("module.a.b") as module_a_b:
module_a_b.return_value = 'new_b'
print('module.a(): ', module.a())
print('module.b(): ', module.b())
try:
mock.patch("module.a.b").__enter__()
assert False, "Attribute error was not raised, test case is broken"
except AttributeError:
pass
module
中的测试文件
# __init__.py
from .a import a
from .a import b
# a.py
def a():
return b()
def b():
return 'b'
结果:
module.a(): b
module.b(): b
--
module.a(): new_b
module.b(): b
我正在尝试使用 unittest.mock
,但出现错误:
AttributeError: does not have the attribute 'get_pledge_frequency'
我有以下文件结构:
pledges/views/
├── __init__.py
├── util.py
└── user_profile.py
pledges/tests/unit/profile
├── __init__.py
└── test_user.py
里面 pledges/views/__init___.py
我有:
from .views import *
from .account import account
from .splash import splash
from .preferences import preferences
from .user_profile import user_profile
在内部,user_profile.py
我有一个名为 user_profile
的函数,它在 util.py
内部调用一个名为 get_pledge_frequency
的函数,如下所示:
def user_profile(request, user_id):
# some logic
# !!!!!!!!!!!!!!!!
a, b = get_pledge_frequency(parameter) # this is the function I want to mock
# more logic
return some_value
我在test_user.py
里面测试如下:
def test_name():
with mock.patch(
"pledges.views.user_profile.get_pledge_frequency"
) as get_pledge_frequency:
get_pledge_frequency.return_value = ([], [])
response = c.get(
reverse("pledges:user_profile", kwargs={"user_id": user.id})
) # this calls the function user_profile inside pledges.user_profile
# some asserts to verify functionality
我查了其他问题,但是当有一个函数称为模块,并且在__init__
文件中导入时,答案不包括。
那么,有什么办法可以解决这个问题吗?我基本上已将文件 user_profile.py
重命名为 profile
然后我更改了测试以引用此模块内的函数,但我想知道是否可以保持函数和模块相同名字.
事实证明,可以模拟在同名模块内的函数中调用的函数。
unittest.mock.patch()
周围的小包装器可以像这样实现:
代码:
from unittest import mock
import importlib
def module_patch(*args):
target = args[0]
components = target.split('.')
for i in range(len(components), 0, -1):
try:
# attempt to import the module
imported = importlib.import_module('.'.join(components[:i]))
# module was imported, let's use it in the patch
patch = mock.patch(*args)
patch.getter = lambda: imported
patch.attribute = '.'.join(components[i:])
return patch
except Exception as exc:
pass
# did not find a module, just return the default mock
return mock.patch(*args)
使用:
而不是:
mock.patch("module.a.b")
你需要:
module_patch("module.a.b")
这是如何工作的?
基本思路是尝试从最长的可能模块路径开始向最短路径导入模块,如果导入成功,则使用该模块作为修补对象。
测试代码:
import module
print('module.a(): ', module.a())
print('module.b(): ', module.b())
print('--')
with module_patch("module.a.b") as module_a_b:
module_a_b.return_value = 'new_b'
print('module.a(): ', module.a())
print('module.b(): ', module.b())
try:
mock.patch("module.a.b").__enter__()
assert False, "Attribute error was not raised, test case is broken"
except AttributeError:
pass
module
中的测试文件
# __init__.py
from .a import a
from .a import b
# a.py
def a():
return b()
def b():
return 'b'
结果:
module.a(): b
module.b(): b
--
module.a(): new_b
module.b(): b