Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking datetime.now using pytest-mock

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
like image 233
Andi Avatar asked Aug 16 '26 22:08

Andi


1 Answers

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

  1. It is often better to patch the module under test rather than the datetime package directly. See also where to patch.
  2. We expect the date time value to be returned, not the mock object (which is what your assertion expects, I think).

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
like image 192
FiddleStix Avatar answered Aug 18 '26 13:08

FiddleStix



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!