Search code examples
pythontestingmockingpython-mock

Making a wrapper for `mock.patch`


Some customized patches from mock.patch I want to use over and over without littering my test code with copy-pastes of the patch setup. e.g. this very handy patch of datetime.date, which, adapted for datetime, would fill my code with

with patch('mymodule.datetime') as mock_datetime:
    mock_datetime.datetime.utcnow.return_value = datetime.datetime(2010, 10, 8, 9, 10)
    mock_date.datetime.side_effect = lambda *args, **kw: datetime.datetime(*args, **kw)

How can I wrap this functionality into a one-line call?


Solution

  • Here's a resource manager class that will do that for you. Since you might want to put it in a separate file from your test classes, it uses inspect to look up the calling module, so that it can pass the correctly qualified target module name to mock.patch.

    import datetime
    import inspect
    # import mock according to your python version
    
    class mock_datetime(object):
    
        def __init__(self, target, new_utcnow):
            self.new_utcnow = new_utcnow
            self.target = target
    
        def __enter__(self):
            calling_module = inspect.getmodule(inspect.stack()[1][0])
            target_from_here = calling_module.__name__ + '.' + self.target
            self.patcher = mock.patch(target_from_here)
            mock_dt = self.patcher.start()
            mock_dt.datetime.utcnow.return_value = self.new_utcnow.replace(tzinfo=None)
            mock_dt.datetime.side_effect = lambda *args, **kw: datetime.datetime(*args, **kw)
            return mock_dt
    
        def __exit__(self, *args, **kwargs):
            self.patcher.stop()
    

    You can then invoke it with

    with mock_datetime('mymodule.datetime', datetime.datetime(2016, 3, 23)):
        assert mymodule.datetime.datetime.utcnow() == datetime.datetime(2016, 3, 23)