Preferred way of patching multiple methods in Python unit test -


i need patch 3 methods (_send_reply, _reset_watchdog , _handle_set_watchdog) mock methods before testing call fourth method (_handle_command) in unit test of mine.

from looking @ documentation mock package, there's few ways go it:

with patch.multiple 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 context manager

def test_handle_command_too_short_v2(self):     simulator = mbg120simulator()      patch.multiple(simulator,                         _send_reply=default,                         _reset_watchdog=default,                         _handle_set_watchdog=default,                         autospec=true) 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 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 think last 1 clearest read, , not result in horribly long lines if number of mocked methods grow. avoids having pass in simulator first (self) argument assert_called_once_with.

but don't find of them particularly nice. multiple patch.object approach, requires careful matching of parameter order nested decorations.

is there approach i've missed, or way make more readable? do when need patch multiple methods on instance/class under test?

no didn't have missed different proposed.

about readability taste decorator way because remove mocking stuff test body... taste.

you right: if patch static instance of method autospec=true must use self in assert_called_* family check methods. case small class because know object need patch , don't need other context patch test method.

you need patch object use test: in tests cannot have instance patch before doing call , in these cases create_autospec cannot used: can patch static instance of methods instead.

if bothered passing instance assert_called_* methods consider use any break dependency. wrote hundreds of test , never had problem arguments order.

my standard approach @ test is

@patch('mbgmodule.mbg120simulator._send_reply', autospec=true) @patch('mbgmodule.mbg120simulator._reset_watchdog', autospec=true) @patch('mbgmodule.mbg120simulator._handle_set_watchdog', autospec=true) def test_handle_command_too_short(self,mock_handle_set_watchdog,                                           mock_reset_watchdog,                                           mock_send_reply):     simulator = mbg120simulator()     simulator._handle_command('xa99')     # can use instead simulator if don't know     mock_send_reply.assert_called_once_with(simulator, 'x?')     self.assertfalse(mock_reset_watchdog.called)     self.assertfalse(mock_handle_set_watchdog_mock.called)     simulator.stop() 
  • patching out of test method code
  • every mock starts mock_ prefix
  • i prefer use simple patch call , absolute path: clear , neat doing

finally: maybe create simulator , stop setup() , teardown() responsibility , tests should take in account patch methods , checks.

i hope answer useful question don't have unique valid answer because readability not absolute concept , depends reader. title speaking general case, question examples specific class of problem should patch methods of object test.

[edit]

i though while question , found bother me: trying test , sense on private methods. when happen first thing should ask why? there lot chances answer because these methods should public methods of private collaborators (that not words).

in new scenario should sense on private collaborators , cannot change object. need patch static instance of other classes.


Comments

Popular posts from this blog

google chrome - Developer tools - How to inspect the elements which are added momentarily (by JQuery)? -

angularjs - Showing an empty as first option in select tag -

php - Cloud9 cloud IDE and CakePHP -