【问题标题】:Mocking a class: Mock() or patch()?模拟一个类:Mock() 还是 patch()?
【发布时间】:2012-01-01 02:52:10
【问题描述】:

我正在将mock 与 Python 一起使用,并且想知道这两种方法中哪一种更好(阅读:更多 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创建mock。代码如下:

@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)

这两种方法都做同样的事情。我不确定这些差异。

有人能告诉我吗?

【问题讨论】:

  • 作为一个从未尝试过Mock()或patch的人,我觉得第一个版本更清晰,并且显示了你想要做什么,尽管我不了解实际的区别。我不知道这是否有任何帮助,但我认为传达一个外行程序员的感受可能会很有用。
  • @MichaelBrennan:感谢您的评论。确实有用。

标签: python unit-testing mocking


【解决方案1】:

mock.patchmock.Mock 是一个非常不同的生物。 patch 用模拟对象替换类并让您使用模拟实例。看看这个sn-p:

>>> 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.patch 通常用于在测试中创建类的新实例的测试。 mock.Mock 实例更清晰,是首选。如果您的self.sut.something 方法创建了MyClass 的实例而不是接收实例作为参数,那么mock.patch 将适合此处。

【讨论】:

  • @D.Shawley 我们如何修补在另一个需要测试的类中实例化的类。
  • @ravz - 阅读"Where to Patch"。这是要正常工作比较困难的事情之一。
  • 我的模拟测试类似于方法二。我希望 MyClass 实例引发异常。我已经尝试了 mock.side_effect 和 mock.return_value.side_effect 并且那些没有工作。我该怎么办?
  • @D.Shawley 链接坏了,现在可以在这里找到:"Where to Patch"
  • 要修补 类对象,请参阅 stackoverflow.com/questions/8469680/…
【解决方案2】:

我有一个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)

【讨论】:

    【解决方案3】:

    解释差异并提供与unittest.mock 合作的指导的要点

    1. 如果要替换被测对象的某些界面元素(传递参数),请使用 Mock
    2. 如果要替换对某些对象的内部调用和被测对象的导入模块,请使用补丁
    3. 始终提供您正在模拟的对象的规范
      • 有了补丁你可以随时提供autospec
      • 使用 Mock,您可以提供 spec
      • 您可以使用 create_autospec 代替 Mock,它旨在创建具有规范的 Mock 对象。

    在上面的问题中,正确答案是使用Mock,或者更准确地说是create_autospec(因为它会将规范添加到您正在模拟的类的模拟方法中),定义的spec on如果尝试调用不存在的类的方法(无论签名),模拟将很有帮助,请查看一些

    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)
    

    具有已定义规范的测试用例使用 fail,因为从 somethingsecond 函数调用的方法不抱怨 MyClass,这意味着 - 它们会捕获错误, 而默认 Mock 将显示。

    附带说明,还有一个选项:使用patch.object 仅模拟被调用的类方法。

    当类被用作函数的内部时,补丁的好用例是这样的:

    def something():
        arg = 1
        return MyClass.method(arg)
    

    然后你会想要使用补丁作为装饰器来模拟 MyClass。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多