samedi 28 mars 2015

Preferred way of patching multiple methods in Python unit test

I need to patch three methods (_send_reply, _reset_watchdog and _handle_set_watchdog) with mock methods before testing a call to a fourth method (_handle_command) in a unit test of mine.


From looking at the documentation for the mock package, there's a few ways I could go about it:


With patch.multiple as decorator



@patch.multiple(MBG120Simulator,
_send_reply=DEFAULT,
_reset_watchdog=DEFAULT,
_handle_set_watchdog=DEFAULT,
autospec=True)
def test_handle_command_too_short_v1(self,
_send_reply,
_reset_watchdog,
_handle_set_watchdog):
simulator = MBG120Simulator()
simulator._handle_command('XA99')
_send_reply.assert_called_once_with(simulator, 'X?')
self.assertFalse(_reset_watchdog.called)
self.assertFalse(_handle_set_watchdog.called)
simulator.stop()


With patch.multiple as context manager



def test_handle_command_too_short_v2(self):
simulator = MBG120Simulator()

with patch.multiple(simulator,
_send_reply=DEFAULT,
_reset_watchdog=DEFAULT,
_handle_set_watchdog=DEFAULT,
autospec=True) as mocks:
simulator._handle_command('XA99')
mocks['_send_reply'].assert_called_once_with('X?')
self.assertFalse(mocks['_reset_watchdog'].called)
self.assertFalse(mocks['_handle_set_watchdog'].called)
simulator.stop()


With multiple patch.object decoratorations



@patch.object(MBG120Simulator, '_send_reply', autospec=True)
@patch.object(MBG120Simulator, '_reset_watchdog', autospec=True)
@patch.object(MBG120Simulator, '_handle_set_watchdog', autospec=True)
def test_handle_command_too_short_v3(self,
_handle_set_watchdog_mock,
_reset_watchdog_mock,
_send_reply_mock):
simulator = MBG120Simulator()
simulator._handle_command('XA99')
_send_reply_mock.assert_called_once_with(simulator, 'X?')
self.assertFalse(_reset_watchdog_mock.called)
self.assertFalse(_handle_set_watchdog_mock.called)
simulator.stop()


Manually replacing methods using create_autospec



def test_handle_command_too_short_v4(self):
simulator = MBG120Simulator()

# Mock some methods.
simulator._send_reply = create_autospec(simulator._send_reply)
simulator._reset_watchdog = create_autospec(simulator._reset_watchdog)
simulator._handle_set_watchdog = create_autospec(simulator._handle_set_watchdog)

# Exercise.
simulator._handle_command('XA99')

# Check.
simulator._send_reply.assert_called_once_with('X?')
self.assertFalse(simulator._reset_watchdog.called)
self.assertFalse(simulator._handle_set_watchdog.called)


Personally I think the last one is clearest to read, and will not result in horribly long lines if the number of mocked methods grow. But I don't find any of them particularly nice. Especially the multiple patch.object approach, which requires careful matching of the parameter order to the nested decorations.


Is there some approach I've missed, or a way to make this more readable? What do you do when you need to patch multiple methods on the instance/class under test?


Aucun commentaire:

Enregistrer un commentaire