我在Python中使用mock并且想知道这两种方法中的哪一种更好(阅读:更多pythonic)。
方法一:只需创建一个模拟对象并使用它。代码如下:
def test_one (self):
mock = Mock()
mock.method.return_value = True
self.sut.something(mock) # This should called mock.method and checks the result.
self.assertTrue(mock.method.called)
方法二:使用补丁创建模拟。代码如下:
@patch("MyClass")
def test_two (self, mock):
instance = mock.return_value
instance.method.return_value = True
self.sut.something(instance) # This should called mock.method and checks the result.
self.assertTrue(instance.method.called)
两种方法都做同样的事情。我不确定这些差异。
有人能开导我吗?
答案 0 :(得分:137)
mock.patch
是一种与mock.Mock
非常不同的生物。 patch
使用模拟对象替换类,并允许您使用模拟实例。看一下这个片段:
>>> class MyClass(object):
... def __init__(self):
... print 'Created MyClass@{0}'.format(id(self))
...
>>> def create_instance():
... return MyClass()
...
>>> x = create_instance()
Created MyClass@4299548304
>>>
>>> @mock.patch('__main__.MyClass')
... def create_instance2(MyClass):
... MyClass.return_value = 'foo'
... return create_instance()
...
>>> i = create_instance2()
>>> i
'foo'
>>> def create_instance():
... print MyClass
... return MyClass()
...
>>> create_instance2()
<mock.Mock object at 0x100505d90>
'foo'
>>> create_instance()
<class '__main__.MyClass'>
Created MyClass@4300234128
<__main__.MyClass object at 0x100505d90>
patch
以允许您控制您调用的函数中类的用法的方式替换MyClass
。修补类后,对类的引用将完全被mock实例替换。
mock.patch
。 mock.Mock
个实例更清晰,更受欢迎。如果您的self.sut.something
方法创建了MyClass
的实例,而不是将实例作为参数接收,则此处mock.patch
将是合适的。
答案 1 :(得分:18)
我对此有YouTube video。
简短回答:当您传递要嘲笑的内容时使用mock
,如果不是,则使用patch
。在这两者中,mock非常受欢迎,因为它意味着您正在编写具有适当依赖注入的代码。
愚蠢的例子:
# Use a mock to test this.
my_custom_tweeter(twitter_api, sentence):
sentence.replace('cks','x') # We're cool and hip.
twitter_api.send(sentence)
# Use a patch to mock out twitter_api. You have to patch the Twitter() module/class
# and have it return a mock. Much uglier, but sometimes necessary.
my_badly_written_tweeter(sentence):
twitter_api = Twitter(user="XXX", password="YYY")
sentence.replace('cks','x')
twitter_api.send(sentence)
答案 2 :(得分:5)
在上面的问题中,正确的答案是使用Mock
,或更精确的选择create_autospec
(因为它将为您正在模拟的类的模拟方法添加规范),如果尝试调用不存在的类的方法(无论是否签名),则模拟上的spec
会很有帮助,请参阅一些
from unittest import TestCase
from unittest.mock import Mock, create_autospec, patch
class MyClass:
@staticmethod
def method(foo, bar):
print(foo)
def something(some_class: MyClass):
arg = 1
# Would fail becuase of wrong parameters passed to methd.
return some_class.method(arg)
def second(some_class: MyClass):
arg = 1
return some_class.unexisted_method(arg)
class TestSomethingTestCase(TestCase):
def test_something_with_autospec(self):
mock = create_autospec(MyClass)
mock.method.return_value = True
# Fails because of signature misuse.
result = something(mock)
self.assertTrue(result)
self.assertTrue(mock.method.called)
def test_something(self):
mock = Mock() # Note that Mock(spec=MyClass) will also pass, because signatures of mock don't have spec.
mock.method.return_value = True
result = something(mock)
self.assertTrue(result)
self.assertTrue(mock.method.called)
def test_second_with_patch_autospec(self):
with patch(f'{__name__}.MyClass', autospec=True) as mock:
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
class TestSecondTestCase(TestCase):
def test_second_with_autospec(self):
mock = Mock(spec=MyClass)
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
def test_second_with_patch_autospec(self):
with patch(f'{__name__}.MyClass', autospec=True) as mock:
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
def test_second(self):
mock = Mock()
mock.unexisted_method.return_value = True
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
具有已定义规范的测试用例使用失败,因为从something
和second
调用的方法对MyClass的功能没有投诉,这意味着-它们会捕获错误,而显示默认Mock
。
作为旁注,还有一个选择:使用patch.object模拟仅使用调用的类方法。
修补程序的好用例就是将类用作函数的内部部分的情况:
def something():
arg = 1
return MyClass.method(arg)
然后,您将需要使用patch作为装饰器来模拟MyClass。