如何使用Python的mock库模拟一个基类

2024-09-28 17:22:35 发布

您现在位置:Python中文网/ 问答频道 /正文

我尝试使用mock用python编写一些单元测试。

例如,我有以下课程:

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

我只想测试handle方法。不需要假设任何关于socketserver.BaseRequestHandler的事情。例如,我想断言handle使用参数1024调用recv。有可能用mock做这种事吗?一、 用mock替换基类socketserver.BaseRequestHandler?还是我偏离了这个想法?


带着埃卡穆尔的回答(谢谢!)我首先尝试了以下方法:

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

但是现在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)给出 <class 'mock.TCPHandler'>

我认为修补基类也会将派生类变成一个模拟类。


我现在尝试了另一个想法:

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

然而,这种模仿似乎不被称为。


Tags: selfwithassertcallmockclasshandlerhandle
3条回答

我认为问题在于你试图模仿你想要测试的实际代码。而不是被代码调用的对象。如果您想知道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)

为了让handler实例化,您可能需要做一些额外的设置,但是基本的思想应该是清楚的。

我不知道这是否是最好的解决方案,但我使用type()用不同的父类重新定义了前面的类。我构建了一个名为patch_parent()的函数,该函数返回具有父模拟的类:

from contextlib import contextmanager

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

在此之后,您可以像这样使用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)

MockFoo类仍然有Foo类的方法,并且它没有在父类中定义方法,因为父类现在是Mock类。

可以通过修补派生类的__bases__来完成此操作:

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

当反转修补程序时,is_local黑客必须停止mock.patch尝试调用delattr

相关问题 更多 >