我在替换调用另一个模块中的函数的简单方法时遇到问题。根据我对模拟的理解,您必须引用被调用的方法(在其上下文中,而不是原始方法)。下面是我正在运行的简化版本,希望它是我需要了解模拟的简单内容。 patch 是否仅用于类和类方法,还是我在这里做错了什么?
谢谢, 史蒂夫
myapp.models.py
from myapp.backends import get_backend
class BasicClass(models.Model):
@staticmethod
def basic_method()
be = get_backend()
print be
myapp.backends._init_.py
def get_backend():
return 'original value'
测试.py
# Referencing the import in myapp.models.basic_class
# vs directly importing myapp.backends
# as indicated here:
# http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch
from myapp.models import get_backend
from myapp.models.basic_class import BasicClass
class ParsersTest(TestCase):
@patch('myapp.models.get_backend')
def test_simplified(self, moves_backend):
# Assertion fails
assert get_backend is moves_backend
# Assuming that assertion fails is why the original return value is always returned
moves_backend.return_value = 'new return value'
BasicClass.basic_method()
最佳答案
使用模拟进行修补的目的是替换对存储在 sys.modules 中的模块的引用,并将其替换为对您的模拟的引用。这意味着修补模块中的代码将接收对模拟对象的引用。
在您的测试中,您正在使用 get_backend
。在应用装饰器之前,它是直接从 myapp.models
导入到测试模块顶部的。它没有修补。您的补丁已就位,但仅适用于 myapp.models
中引用其中导入的 get_backend
符号的代码。
我知道这很令人困惑。对我来说,这是开始模拟最困难的部分。如果您的测试如下所示:
class ParsersTest(TestCase):
@patch('myapp.models.get_backend')
def test_simplified(self, moves_backend):
from myapp.models.basic_class import BasicClass
# Assertion should pass
BasicClass.basic_method()
moves_backend.assert_called_with()
moves_backend.return_value = 'new return value'
# As should this one (if you change the method to return instead of print)
self.assertEqual(BasicClass.basic_method(), 'new return value')
我认为你的测试会通过。这里的主要区别是您没有直接针对 get_backend 进行测试。您正在测试一种在应用补丁后使用导入的 get_backend 的方法。
更新
在没有接触到你的实际代码的情况下,我能想到的唯一一件事就是我不喜欢使用补丁作为装饰器,因为你对补丁何时应用/删除的控制较少,并且担心通过 args 获取对模拟的引用。
尝试上下文管理器风格:
with mock.patch('my app.models.get_backend') as moves_backend:
#...
其余测试逻辑嵌套在该分支下。
更新第二部分
我刚刚在您的原始代码中注意到BasicClass
位于myapp.models.basic_class.py
中。
如果是这种情况,您应该将修补程序应用于 'myapp.models.basic_class.get_backend'
,因为您想要修补对正在导入的 get_backend
的引用在myapp.models.basic_class
子模块中。
关于带有子模块的 Python 模拟补丁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21559157/