Python etc / Patch built-ins

Patch built-ins

The unittest.mock.patch decorator can replace any attribute of any module with MagicMock. That can be used for unit-testing as a replacement for other code isolation techniques such as dependency injection.

@patch('sms.send')
def test_now(patched_send):
    create_client('31205551111')
    patched_send.assert_called_with(
        '31205551111',
        'client created'
    )   
test_now()

The significant limitation is that patch doesn't work with built-ins:

# foo.py
from datetime import datetime
def is_odd_hour_now():
    print('@@@', datetime)
    print('@@@', datetime.now)
    return datetime.now().hour % 2 == 1
# test_foo.py
from datetime import datetime
from unittest.mock import patch
from foo import is_odd_hour_now

@patch('datetime.datetime.now')
def test_is_odd_hour_now(patched_now):
    patched_now.return_value = \
        datetime(2010, 1, 1, 13, 0, 0)
    assert is_odd_hour_now()

That will cause TypeError: can't set attributes of built-in/extension type 'datetime.datetime'.

As a workaround you can patch not the original datetime, but the reference that foo holds:

from datetime import datetime
from unittest.mock import patch
from foo import is_odd_hour_now

@patch('foo.datetime')
def test_is_odd_hour_now(patched_datetime):
    patched_datetime.now.return_value = \
        datetime(2010, 1, 1, 13, 0, 0)