I try to use mock
to write some unit-tests in 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. Without having to assume anything about socketserver.BaseRequestHandler
. I for example want to assert that handle
calls recv
with the argument 1024
. Is it possible to do such thing with mock? I.e. replacing the base class socketserver.BaseRequestHandler
with a mock? Or am I off track with that idea?
With the answer of ecatmur (thank you!) I first tried the following:
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:
['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'>
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.
mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects and make assertions about how they have been used. unittest. mock provides a core Mock class removing the need to create a host of stubs throughout your test suite.
Mock Entire Class To mock an entire class you'll need to set the return_value to be a new instance of the class. @mock.
You can do this by patching the derived class's __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.
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.
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.
I don't know if it is the best solution but I managed redefining the previous class with a different parent using type(). I built a function called patch_parent()
, that returns the class with a parent mock:
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With