简体   繁体   English

如何使用python模拟库模拟基类

[英]How to mock a base class with python mock library

I try to use mock to write some unit-tests in python. 我尝试使用mock在python中编写一些单元测试。

For example I have the following class: 例如,我有以下课程:

class TCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()

And I only want to test the handle method. 我只想测试handle方法。 Without having to assume anything about socketserver.BaseRequestHandler . 无需假设有关socketserver.BaseRequestHandler任何内容。 I for example want to assert that handle calls recv with the argument 1024 . 我例如想要断言handle调用带有参数1024 recv Is it possible to do such thing with mock? 是否有可能用模拟做这样的事情? Ie replacing the base class socketserver.BaseRequestHandler with a mock? 即用模拟替换基类socketserver.BaseRequestHandler Or am I off track with that idea? 或者我是否偏离了这个想法?


With the answer of ecatmur (thank you!) I first tried the following: 有了ecatmur的答案(谢谢!)我首先尝试了以下内容:

patcher = patch.object(TCPHandler, '__bases__', (Mock,))
with patcher:
    patcher.is_local = True
    handler = TCPHandler()
    handler.handle()

But now handle is not called anylonger and dir(handler) gives: 但是现在handle不再调用,而dir(handler)给出:

['assert_any_call', 'assert_called_once_with', 'assert_called_with', 'assert_has_calls', 'attach_mock', 'call_args', 'call_args_list', 'call_count', 'called', 'configure_mock', 'method_calls', 'mock_add_spec', 'mock_calls', 'reset_mock', 'return_value', 'side_effect']

type(handler) gives <class 'mock.TCPHandler'> type(handler)给出<class 'mock.TCPHandler'>

Which I interpret that patching the base class also turns my derived class into a mock. 我解释修补基类也将我的派生类变成了一个模拟。


I now gave another idea a try: 我现在尝试了另一个想法:

mock = MagicMock()
TCPHandler.handle(mock)
#assertions

However the mock seems not to be called. 然而,模拟似乎没有被称为。

You can do this by patching the derived class's __bases__ : 你可以通过修补派生类的__bases__来做到这__bases__

def test_derived():
    patcher = mock.patch.object(Derived, '__bases__', (mock.Mock,))
    with patcher:
        patcher.is_local = True
        d = Derived()
        print d.foo()

The is_local hack is necessary to stop mock.patch from trying to call delattr when reversing the patch. is_local黑客必须停止mock.patch从试图调用delattr倒车修补程序时。

I don't know if it is the best solution but I managed redefining the previous class with a different parent using type() . 我不知道它是否是最好的解决方案但是我使用type()管理了使用不同父级重新定义上一个类。 I built a function called patch_parent() , that returns the class with a parent mock: 我构建了一个名为patch_parent()的函数,它返回带有父模拟的类:

from contextlib import contextmanager

@contextmanager
def patch_parent(class_):
    """
    Mock the bases
    """
    yield type(class_.__name__, (Mock,), dict(class_.__dict__))

After this, you can use the patch_parent like this: 在此之后,您可以像这样使用patch_parent

class Bar():
   def method(self, param1, param2...):
       ...

class Foo(Bar):
   pass


>>> with patch_parent(Foo) as MockFoo:
...     f = MockFoo()
...     print f
...     print f.method()
... 
<Foo id='15488016'>
<Foo name='mock.method()' id='15541520'>
>>> s = Foo()
>>> s.method()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method() takes exactly 3 arguments (1 given)

The MockFoo class still has the methods of the Foo class and it doesn't have the methods defined in the parent because the parent is now a Mock class. MockFoo类仍然具有Foo类的方法,并且它没有在父类中定义的方法,因为父类现在是Mock类。

I think the problem is really that you are trying to mock the actual code you want to test. 我认为问题实际上是你试图模拟你想要测试的实际代码。 Rather than the objects that are being called by that code. 而不是该代码调用的对象。 If you are interested in seeing whether the handle method calls the recv method on self.request then mock out the recv method. 如果你有兴趣看看handle方法是否在self.request上调用recv方法,那么就模拟出recv方法。

def test_tcp_handler_method(self):

    handler = TCPHandler()
    handler.request = Mock()

    handler.handle()

    self.assertTrue(handler.request.recv.called)
    self.assertEqual(handler.request.recv.call_args[0], 1024)

You might have to do some extra setup in order to get handler to instantiate but the basic idea should be clear. 你可能需要做一些额外的设置才能让处理程序实例化,但基本的想法应该是清楚的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM