How to mock django timezone.now in a abstract model

I'm unable to mock timezone.now in the model's created field. The test fails due unmocked/current datetime.

I tried patch('django.utils.timezone.now') to no avail.

# app/models.py
from django.utils import timezone
class BaseModel(models.Model):
    created = models.DateTimeField(default=timezone.now, editable=False)

    class Meta:
        abstract = True

class MyModel(BaseModel):
    ...
# app/tests/test.py
from app.models import MyModel
from datetime import dateHow to mock django timezone.now in a abstract modeltime
from django.utils import timezone

def test_(self):
    dt=datetime(2018, 1, 24, tzinfo=timezone.utc)
    with patch('app.models.timezone.now', return_value=dt):
        instance = MyModel.objects.create()
        assert instance.created == dt

You either need to patch the default of the model or patch the 'django.utils.timezone.now' before you import the model you are testing:

from unittest.mock import patch
from datetime import datetime
from django.utils import timezone

def test_created_field_patch_before_import(monkeypatch):
    dt = datetime(2018, 1, 24, tzinfo=timezone.utc)

    with patch("django.utils.timezone.now", return_value=dt):
        # Import AFTER patch so that default=timezone.now captures the mocked function
        from app.models import MyModel
        
        instance = MyModel.objects.create()
        assert instance.created == dt

This might not work for other models/tests if you are testing more than one model. A better way (IMHO) is to patch the default of the model:

from datetime import datetime
from django.utils import timezone
from app.models import MyModel


def test_created_default_patching(db):
    dt = datetime(2018, 1, 24, tzinfo=timezone.utc)

    field = MyModel._meta.get_field("created")

    # store original default so we can restore it
    original_default = field.default

    # patch
    field.default = lambda: dt

    try:
        instance = MyModel.objects.create()
        assert instance.created == dt
    finally:
        # restore after the test so other tests do NOT break
        field.default = original_default

This test is isolated and dont mess with other tests that come after.

Вернуться на верх