10

I'd like to mock timing so that be able to set certain time to a field of type DateTimeField with auto_now_add=True during my tests e.g:

class MyModel:
    ...
    created_at = models.DateTimeField(auto_now_add=True)
    ...


class TestMyModel(TestCase):
    ...
    def test_something(self):
        # mock current time so that `created_at` be something like 1800-02-09T020000
        my_obj = MyModel.objects.create(<whatever>)
        # and here my_obj.created_at == 1800-02-09T000000

I'm aware the current date is always used for this type of fields, that is why I'm looking for an alternative to mock somehow the system timing, but just in a context.

I've tried some approaches, for instance, creating a context with freeze_time but didn't work:

with freeze_now("1800-02-09"):
    MyModel.objects.create(<whatever>)
    # here the created_at doesn't fit 1800-02-09

Ofc I guess, this is due to the machinery behind the scene to create the object when auto_now_add=True.

I don't want to remove auto_now_add=True and/or use default values.

Is there a way we can mock the timing so that we can make this type of field to get the time that I want in certain context?

I'm using Django 1.9.6 and Python 3.4

trinchet
  • 6,753
  • 4
  • 37
  • 60

3 Answers3

17

Okay, I have found a solution, it is based on mock:

def mock_now():
    return <mock time>

class TestMyModel(TestCase):
    ...
    @mock.patch('django.utils.timezone.now', mock_now)
    def test_as_decorator(self):
        ...
        my_obj = MyModel.objects.create(<whatever>)
        ...
         # here the created_at field has the mocked time :)

    def test_as_context_manager(self):
         mocked_dt = datetime.datetime(2015, 9, 3, 11, 15, 0)
         with mock.patch('django.utils.timezone.now', mock.Mock(return_value=mocked_dt)):
             my_obj = MyModel.objects.create(<whatever>)
             # here the created_at field has the mocking time :)
trinchet
  • 6,753
  • 4
  • 37
  • 60
3

Expanding on @trinchets answer, here it is as context manager,

from datetime import timedelta
from django.utils import timezone
from mock import patch, Mock


last_week = timezone.now() - timedelta(weeks=1)
with patch('django.utils.timezone.now', Mock(return_value=last_week)):
    # Make your models/factories

# Carry on with normal time
farridav
  • 933
  • 8
  • 18
0

You can try this

from datetime import datetime

from django.test import TestCase
from mock import patch


class TestMyModel(TestCase):
    @patch('django.utils.timezone.now', )
    def test_something(self, mock_now):
        mock_now.return_value = datetime(2015, 9, 3, 11, 15, 0)
Sarath Ak
  • 7,903
  • 2
  • 47
  • 48