A short post explaining how to mock `django.utils.timezone.now` in unit tests.
I recently had to implement a Django model that represents a
competition with a
start_time field and an
To determine whether the competition is active, I get the current
django.utils.timezone.now() and check if it's within
the time interval between
So far so good, the method looks like this:
# models.py from django.db import models from django.utils import timezone class Competition(models.Model): start_time = models.DateTimeField() end_time = models.DateTimeField() def is_active(self) -> bool: now = timezone.now() return now >= self.start_time and now < self.end_time
When we run the code,
timezone.now() will return a
object representing the current time, and this is an issue
because unit tests should be deterministic and always yield the
same results. We cannot write tests that will yield different
results depending on the time they're run at.
This is where mocking comes in: in our unit tests we will mock
timezone.now() so that it returns the same
# test_models.py from datetime import datetime, timezone from unittest.mock import patch from .models import Competition # `timezone.now` will now always return the value we want @patch( "django.utils.timezone.now", lambda: datetime(2022, 1, 15, tzinfo=timezone.utc), ) def test_Competition_is_active(): competition = Competition( start_time=datetime(2021, 12, 1, tzinfo=timezone.utc), end_time=datetime(2022, 1, 31, tzinfo=timezone.utc), ) assert competition.is_active() is True competition = Competition( start_time=datetime(2021, 12, 1, tzinfo=timezone.utc), end_time=datetime(2021, 1, 31, tzinfo=timezone.utc), ) assert competition.is_active() is False
Now that we don't have to worry about the runtime value of
timezone.now, we can run our CI/CD pipelines with the same
results on every run, and avoid unexpected failures.