Giuseppe Fantoni
Giuseppe Fantoni

Reputation: 333

Unit-testing datetime

Can someone help me to unit test this line of code?

from datetime import datetime, timedelta, timezone

def get_timestamp_plus_100_year():
    return int((datetime.now(timezone.utc) + timedelta(days=100 * 365)).timestamp())

I try this, but I don't know how to assign the values:

@patch("src.shared.utils.timedelta")
@patch("src.shared.utils.datetime")
def test_get_timestamp_now_plus_100_years(self, mock_datetime, mock_timedelta):
    mock_datetime.now.return_value = 2021-09-14 15:54:25.284087+00:00
    mock_timedelta.return_value = 36500 days, 0:00:00

    self.assertEqual(
        get_timestamp_plus_100_year(),
        int((mock_datetime.now.return_value 
             + mock_timedelta.return_value).timestamp_return_value ),
    )

Upvotes: 1

Views: 1736

Answers (1)

Niel Godfrey P. Ponciano
Niel Godfrey P. Ponciano

Reputation: 10699

Correct the implementation first. As pointed out by @MrFuppes, not all years are 365 days. Assuming today is 2021-9-15, your original implementation would result to:

>>> datetime.now(timezone.utc) + timedelta(days=100 * 365)
datetime.datetime(2121, 8, 22, 9, 19, 30, 468735, tzinfo=datetime.timezone.utc)

Here, you can see that instead of the expected 2121-9-15, what we got was 2121-8-22.

Option 1: Using datetime.replace() to replace the year with year+100

>>> (dt := datetime.now(timezone.utc)).replace(year=dt.year + 100)
datetime.datetime(2121, 9, 15, 9, 24, 52, 139984, tzinfo=datetime.timezone.utc)

Option 2: Using dateutil.relativedelta.relativedelta to add +100 years. This requires pip install python-dateutil.

>>> datetime.now(timezone.utc) + relativedelta(years=100)
datetime.datetime(2121, 9, 15, 9, 28, 16, 789807, tzinfo=datetime.timezone.utc)

Then, you don't need to mock the timedelta (or relativedelta in our corrected code) since its value will always be 100 years. You just need to mock the current date via datetime.now() since it is the base of the addition and we need to assert the result.

Assuming this is the file tree:

.
├── src.py
└── test_src.py

src.py

from datetime import datetime, timezone

from dateutil.relativedelta import relativedelta


def get_timestamp_plus_100_year():
    return int((datetime.now(timezone.utc) + relativedelta(years=100)).timestamp())

Solution 1:

from datetime import datetime
import unittest
from unittest.mock import patch

from dateutil.relativedelta import relativedelta

from src import get_timestamp_plus_100_year


class TestDates(unittest.TestCase):
    @patch("src.datetime")
    def test_get_timestamp_now_plus_100_years(self, mock_datetime):
        frozen_dt = datetime(year=2021, month=9, day=15)
        mock_datetime.now.return_value = frozen_dt

        # Solution 1.1
        self.assertEqual(
            get_timestamp_plus_100_year(),
            int((frozen_dt + relativedelta(years=100)).timestamp()),
        )

        # Solution 1.2
        frozen_dt_100 = datetime(year=2121, month=9, day=15)  # Since we already know the value of +100 years, we can just define it here
        self.assertEqual(
            get_timestamp_plus_100_year(),
            int(frozen_dt_100.timestamp()),
        )

Solution 2

This requires freezegun via pip install freezegun

from datetime import datetime
import unittest

from freezegun import freeze_time

from src import get_timestamp_plus_100_year


@freeze_time("2021-09-15")  # Either here
class TestDates(unittest.TestCase):
    @freeze_time("2021-09-15")  # Or here
    def test_get_timestamp_now_plus_100_years_2(self):
        frozen_dt_100 = datetime(year=2121, month=9, day=15)  # Since we already know the value of +100 years, we can just define it here
        self.assertEqual(
            get_timestamp_plus_100_year(),
            int(frozen_dt_100.timestamp()),
        )

Upvotes: 2

Related Questions