Amin Ba
Amin Ba

Reputation: 2436

How to partially mock patch datetime in pytest python?

Suppose I have a module that checks if an input date is today. If it is today, shows tomorrow, otherwise, returns False

# my_module.py
import datetime


def is_today_return_tomorrow(input_date):
    if input_date != datetime.date.today():
        return False
    return input_date + datetime.timedelta(1)

The purpose of this example is having a function that have both datetime.date.today() and datetime.timedelta(1) and it is a simplified code to explain the question.

Now I want to test this code using pytest.

# test_my_module.py
import datetime
from unittest.mock import patch

from my_module import is_today_return_tomorrow


def test_is_today():
    cases = [
        {'input_date': datetime.date(2022, 5, 14), 'expected_output': datetime.date(2022, 5, 15), 'today': datetime.date(2022, 5, 14)},
        {'input_date': datetime.date(2022, 5, 14), 'expected_output': False, 'today': datetime.date(2022, 5, 12)}
    ]
    for case in cases:
        class FakeDatetime:
            class date:
                def today(): return case['today']
        with patch('my_module.datetime', FakeDatetime)
            assert is_today_return_tomorrow(case['input_date']) == case['expected_output']

This is the answer explained in this question how to mock patch `today` in pytest python?

However, there is now a problem. Because I am patching my_module.datetime, it no longer has timedelta method. How should I address this problem?

Upvotes: 0

Views: 648

Answers (1)

MrBean Bremen
MrBean Bremen

Reputation: 16815

You can just patch datetime.date instead of the whole datetime module:

@pytest.mark.parametrize("input_data, expected_output, today",
                         [(datetime.date(2022, 5, 14),
                           datetime.date(2022, 5, 15),
                           datetime.date(2022, 5, 14)),
                          (datetime.date(2022, 5, 14),
                           False,
                           datetime.date(2022, 5, 12))])
def test_is_today(input_data, expected_output, today):
    with patch('my_module.datetime.date') as date:
        date.today.return_value = today
        assert is_today_return_tomorrow(input_data) == expected_output

This leaves other datetime functions like timedelta intact and can use them.

Note that I have replaced the iteration over test cases by standard pytest parametrization, as you are using pytest anyway.

Upvotes: 2

Related Questions