I have a function func_to_mock
in module module_to_mock.py
. My unit test is located in test_func_to_mock.py
I am trying to mock datetime.datetime.now
, however, I am struggling. I get the error TypeError: cannot set 'now' attribute of immutable type 'datetime.datetime'
.
What am I missing here?
module_to_mock.py:
# module_to_mock.py
import datetime
def func_to_mock() -> datetime.datetime:
return datetime.datetime.now()
My unit test:
# test_func_to_mock.py
import datetime
from pytest_mock import MockFixture
import port_bt.module_to_mock
def test_func_to_mock(mocker: MockFixture) -> None:
# This will error with:
# TypeError: cannot set 'now' attribute of immutable type 'datetime.datetime'
mocked_date = mocker.patch(
"datetime.datetime.now", return_value=datetime.datetime(2023, 1, 31, 0, 0, 0)
)
assert port_bt.module_to_mock.func_to_mock() == mocked_date
As you've seen, you cannot patch Python standard library classes that are implemented in C (according to these unittest.mock
docs). However, you can patch the datetime
package like so:
test_func_to_mock.py
import datetime
from pytest_mock import MockFixture
import module_to_mock
def test_func_to_mock(mocker: MockFixture) -> None:
mocked_datetime = mocker.patch(
"module_to_mock.datetime", # 1
)
jan_31 = datetime.datetime(2023, 1, 31, 0, 0, 0)
mocked_datetime.datetime.now.return_value = jan_31
assert module_to_mock.func_to_mock() == jan_31 # 2
Note that
In response to the question about fixtures, I would say that sounds like a very good idea. You should be able to yield
the mock object from a fixture so that you don't have to patch()
in every test function:
test_func_with_fixture.py
import datetime
from typing import Final, Generator
from unittest.mock import MagicMock
import pytest
from pytest_mock import MockFixture
import module_to_mock
JAN_31: Final[datetime.datetime] = datetime.datetime(2023, 1, 31, 0, 0, 0)
@pytest.fixture
def datetime_fixture(mocker: MockFixture) -> Generator[MagicMock, None, None]:
mocked_datetime = mocker.patch(
"module_to_mock.datetime",
)
mocked_datetime.datetime.now.return_value = JAN_31
yield mocked_datetime
def test_func_to_mock(datetime_fixture: MagicMock) -> None:
assert module_to_mock.func_to_mock() == JAN_31