我无法替换在另一个模块中调用函数的简单方法。根据我对模拟的理解,你必须引用被调用的方法(在它的上下文中,而不是原始的)。下面是我正在运行的简化版本,并希望我需要了解有关模拟的简单内容。补丁是否仅用于Class和Class方法,或者我在这里做错了什么?
谢谢, 史蒂夫
myapp.models.py
from myapp.backends import get_backend
class BasicClass(models.Model):
@staticmethod
def basic_method()
be = get_backend()
print be
myapp.backends ._ 初始化 _。PY
def get_backend():
return 'original value'
test.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()
答案 0 :(得分:0)
使用mock进行修补的目的是替换对模块的引用,因为它将存储在sys.modules
中,并将其替换为对模拟的引用。这意味着修补模块中的代码 将接收对模拟对象的引用。
在您的测试中,您正在使用get_backend
。在应用装饰器之前,直接从myapp.models
导入了测试模块的顶部。它没有打补丁。您的补丁已到位,但仅适用于引用此处导入的myapp.models
符号的get_backend
中的代码。
我知道这令人困惑。对我来说,这是开始使用mock最困难的部分。如果你的测试看起来像这样:
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获取对mock的引用。
尝试上下文管理器样式:
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
的引用。
答案 1 :(得分:0)
我觉得你误解了。记住:在Python中,模块和类成员(或多或少)只是简单的变量。它们可能碰巧包含一种方法,但它们仍然只是变量。当您从另一个模块导入某些内容时,它只会在导入模块上创建一个变量并将一些对象放入其中。
装饰器@patch('myapp.models.get_backend')
只是用模拟对象替换myapp.models
上的变量。这不是你想要的。到应用修补程序时,myapp.models.basic_class
已经导入,因此它已经从myapp.models
导入了对实际方法的引用。 (换句话说,它的get_backend
变量已经保存了实际方法。)您想要替换myapp.models.basic_class
中实际使用的变量,如下所示:
@patch('myapp.models.basic_class.get_backend')
这将使用模拟填充get_backend
上的myapp.models.basic_class
变量。因此,当您致电BasicClass.basic_method()
时,basic_method
会查看myapp.models.basic_class.get_backend
变量并查找模拟内容。
所以试试这个:
from myapp.models import basic_class
class ParsersTest(TestCase):
@patch('myapp.models.basic_class.get_backend')
def test_simplified(self, moves_backend):
assert basic_class.get_backend is moves_backend
moves_backend.return_value = 'new return value'
basic_class.BasicClass.basic_method()
所有这一切,要小心你的测试。你不想以各种可能的方式测试一切。考虑一下这个测试在编写之前给你的价值。