There are many ways to use Python Mock’s patcher, but sometimes there’s something in your test module you’ll always need to mock. It’s possible to apply the patcher to the entire class, but it still means you have to litter your tests with parameters. To make things a bit tidier and DRYer, I started using the following mock factory in my tests to create instance-level mocks.
def add_mock(self, target, *args, **kwargs): target_obj = target.split('.')[-1] patcher = patch(target, *args, **kwargs) if target_obj[0].isupper(): # Assume class mock attr = 'Mock' else: attr = 'mock_' attr += target_obj setattr(self, attr, patcher.start()) self.addCleanup(patcher.stop)
The above attribute factory will add new instance level attributes, named either mock_some_variable
for imports that look like variables or functions, or MockSomeObject
for imports that look like objects. It will use the patcher’s patch
function directly, and assign the new attribute to the current test object instance, then stop the patch at clean up.
This can be used in your tests’ setup method like so.
def setUp(self): module = 'my_app.management.commands.send_monthly_report' self.add_mock('%s.settings' % module) # Pass patch arguments through the setup method self.add_mock('%s.send_mail' % module, autospec=True) # We now have an instance-level mock to work with self.mock_settings.REPORT_TEMPLATE = 'test-template'
Now all your test methods have instance attributes to work with instead of a long line of parameters. Much better!
def test_the_first(self): function_to_test() self.assertTrue(self.mock_send_mail.called)