使用 patch to 模拟一个函数(而不是一个方法)
Using patch to to mock a function (as opposed to a method)
我想做类似下面的例子(找到here)
>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
... thing = ProductionClass()
... thing.method(1, 2, 3)
然而,这是在 ProductionClass
上修补名为 method
的方法。我想在上下文中修补通用函数。理想情况下看起来像...
with path.something(my_fn, return_value=my_return) as mock_function:
do_some_other_fn()
my_fn
在 do_some_other_fn
中被调用得很深,因此很难直接模拟出来。这似乎应该是直截了当的,但我找不到正确的语法
EDIT 在 do_some_other_fn
所在的模块中,我导入 my_fn
如下
from my_module import my_fn
所以我需要一种方法来告诉 mock 从模块外部对其进行修补。这可能吗?
编辑 2 我认为这让我更清楚我在寻找什么
这可行但不理想:
import my_module
with patch('my_module.fn', return_value='hello') as patch_context:
x = my_module.fn()
# x now contains 'hello'
但是我更希望它像这样(或类似的东西)工作
from my_module import fn
with patch('my_module.fn', return_value='hello') as patch_context:
x = fn()
# x contains real result from real call to fn()
你可以看到类似模块对象静态方法的函数。要修补模块 mymodule
中的函数 func
,您可以使用
patch("mymodule.func", return_value=my_return)
您应该注意 Where to patch,如果该函数在您进行测试的同一模块中,则应使用 "__main__.func"
作为补丁参数。
patch
像 patch.object
可以用作装饰器、上下文或通过 start()
和 stop()
方法。
现在,当您在一个模块中从另一个模块导入函数时,例如:
from mymodule import func as foo
您在名为 foo
的新模块中创建了对 func
的新引用。 每个
在此模块中调用 foo
的时间,您将使用导入时加载的对 mymodule.func
的引用:如果您想更改此行为,您应该在新模块中修补 foo
。
为了更清楚地说明,我构建了一个示例,其中 mymodule
包含 func
,module_a
包含 mymodule
并使用 mymodule.func
, module_b
使用 from mymodule import func as foo
并使用机器人 foo
和 mymodule.func
mymodule.py
def func():
return "orig"
module_a.py
import mymodule
def a():
return mymodule.func()
module_b.py
from mymodule import func as foo
import mymodule
def b_foo():
return foo()
def b():
return mymodule.func()
test.py
import unittest
from unittest.mock import *
import mymodule
import module_a
import module_b
class Test(unittest.TestCase):
def test_direct(self):
self.assertEqual(mymodule.func(), "orig")
with patch("mymodule.func", return_value="patched"):
self.assertEqual(mymodule.func(), "patched")
def test_module_a(self):
self.assertEqual(module_a.a(), "orig")
with patch("mymodule.func", return_value="patched"):
self.assertEqual(module_a.a(), "patched")
def test_module_b(self):
self.assertEqual(module_b.b(), "orig")
with patch("mymodule.func", return_value="patched"):
self.assertEqual(module_b.b(), "patched")
self.assertEqual(module_b.b_foo(), "orig")
with patch("module_b.foo", return_value="patched"):
self.assertEqual(module_b.b(), "orig")
self.assertEqual(module_b.b_foo(), "patched")
if __name__ == '__main__':
unittest.main()
换句话说,选择修补位置的真正规则是如何在 您想使用修补版本 的地方引用函数。
您尝试使用 from my_module import fn
进行修补无效,因为 import 语句创建了一个本地符号 fn
,它指向 fn
在 my_module
中的任何值 导入时。您稍后修补 my_module.fn
但这并不重要,因为您已经拥有 fn
.
的本地副本
如果包含 patch
调用的文件是主模块(python
最初加载的文件),您应该可以通过修补 __main__.fn
:
from my_module import fn
with patch('__main__.fn', return_value='hello') as patch_context:
x = fn()
如果包含 patch
调用的文件作为模块从主模块加载,则 __main__
将不起作用,您需要传递包含的模块的绝对模块名称你 patch
呼叫 patch
而不是 __main__
.
我想做类似下面的例子(找到here)
>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
... thing = ProductionClass()
... thing.method(1, 2, 3)
然而,这是在 ProductionClass
上修补名为 method
的方法。我想在上下文中修补通用函数。理想情况下看起来像...
with path.something(my_fn, return_value=my_return) as mock_function:
do_some_other_fn()
my_fn
在 do_some_other_fn
中被调用得很深,因此很难直接模拟出来。这似乎应该是直截了当的,但我找不到正确的语法
EDIT 在 do_some_other_fn
所在的模块中,我导入 my_fn
如下
from my_module import my_fn
所以我需要一种方法来告诉 mock 从模块外部对其进行修补。这可能吗?
编辑 2 我认为这让我更清楚我在寻找什么
这可行但不理想:
import my_module
with patch('my_module.fn', return_value='hello') as patch_context:
x = my_module.fn()
# x now contains 'hello'
但是我更希望它像这样(或类似的东西)工作
from my_module import fn
with patch('my_module.fn', return_value='hello') as patch_context:
x = fn()
# x contains real result from real call to fn()
你可以看到类似模块对象静态方法的函数。要修补模块 mymodule
中的函数 func
,您可以使用
patch("mymodule.func", return_value=my_return)
您应该注意 Where to patch,如果该函数在您进行测试的同一模块中,则应使用 "__main__.func"
作为补丁参数。
patch
像 patch.object
可以用作装饰器、上下文或通过 start()
和 stop()
方法。
现在,当您在一个模块中从另一个模块导入函数时,例如:
from mymodule import func as foo
您在名为 foo
的新模块中创建了对 func
的新引用。 每个
在此模块中调用 foo
的时间,您将使用导入时加载的对 mymodule.func
的引用:如果您想更改此行为,您应该在新模块中修补 foo
。
为了更清楚地说明,我构建了一个示例,其中 mymodule
包含 func
,module_a
包含 mymodule
并使用 mymodule.func
, module_b
使用 from mymodule import func as foo
并使用机器人 foo
和 mymodule.func
mymodule.py
def func():
return "orig"
module_a.py
import mymodule
def a():
return mymodule.func()
module_b.py
from mymodule import func as foo
import mymodule
def b_foo():
return foo()
def b():
return mymodule.func()
test.py
import unittest
from unittest.mock import *
import mymodule
import module_a
import module_b
class Test(unittest.TestCase):
def test_direct(self):
self.assertEqual(mymodule.func(), "orig")
with patch("mymodule.func", return_value="patched"):
self.assertEqual(mymodule.func(), "patched")
def test_module_a(self):
self.assertEqual(module_a.a(), "orig")
with patch("mymodule.func", return_value="patched"):
self.assertEqual(module_a.a(), "patched")
def test_module_b(self):
self.assertEqual(module_b.b(), "orig")
with patch("mymodule.func", return_value="patched"):
self.assertEqual(module_b.b(), "patched")
self.assertEqual(module_b.b_foo(), "orig")
with patch("module_b.foo", return_value="patched"):
self.assertEqual(module_b.b(), "orig")
self.assertEqual(module_b.b_foo(), "patched")
if __name__ == '__main__':
unittest.main()
换句话说,选择修补位置的真正规则是如何在 您想使用修补版本 的地方引用函数。
您尝试使用 from my_module import fn
进行修补无效,因为 import 语句创建了一个本地符号 fn
,它指向 fn
在 my_module
中的任何值 导入时。您稍后修补 my_module.fn
但这并不重要,因为您已经拥有 fn
.
如果包含 patch
调用的文件是主模块(python
最初加载的文件),您应该可以通过修补 __main__.fn
:
from my_module import fn
with patch('__main__.fn', return_value='hello') as patch_context:
x = fn()
如果包含 patch
调用的文件作为模块从主模块加载,则 __main__
将不起作用,您需要传递包含的模块的绝对模块名称你 patch
呼叫 patch
而不是 __main__
.