Django unit testing with date/time-based objects
Question: Suppose I have the following `Event` model:

    
    
    from django.db import models
    import datetime
    
    class Event(models.Model):
        date_start = models.DateField()
        date_end = models.DateField()
    
        def is_over(self):
            return datetime.date.today() > self.date_end
    

I want to test `Event.is_over()` by creating an Event that ends in the future
(today + 1 or something), and stubbing the date and time so the system thinks
we've reached that future date.

I'd like to be able to stub ALL system time objects as far as python is
concerned. This includes `datetime.date.today()`, `datetime.datetime.now()`,
and any other standard date/time objects.

What's the standard way to do this?


Answer: **EDIT** : Since my answer is the accepted answer here I'm updating it to let
everyone know a better way has been created in the meantime, the freezegun
library: <https://pypi.python.org/pypi/freezegun>. I use this in all my
projects when I want to influence time in tests. Have a look at it.

**Original answer:**

Replacing internal stuff like this is always dangerous because it can have
nasty side effects. So what you indeed want, is to have the monkey patching be
as local as possible.

We use Michael Foord's excellent mock library:
<http://www.voidspace.org.uk/python/mock/> that has a `@patch` decorator which
patches certain functionality, but the monkey patch only lives in the scope of
the testing function, and everything is automatically restored after the
function runs out of its scope.

The only problem is that the internal `datetime` module is implemented in C,
so by default you won't be able to monkey patch it. We fixed this by making
our own simple implementation which _can_ be mocked.

The total solution is something like this (the example is a validator function
used within a Django project to validate that a date is in the future). Mind
you I took this from a project but took out the non-important stuff, so things
may not actually work when copy-pasting this, but you get the idea, I hope :)

First we define our own very simple implementation of `datetime.date.today` in
a file called `utils/date.py`:

    
    
    import datetime
    
    def today():
        return datetime.date.today()
    

Then we create the unittest for this validator in `tests.py`:

    
    
    import datetime
    import mock
    from unittest2 import TestCase
    
    from django.core.exceptions import ValidationError
    
    from .. import validators
    
    class ValidationTests(TestCase):
        @mock.patch('utils.date.today')
        def test_validate_future_date(self, today_mock):
            # Pin python's today to returning the same date
            # always so we can actually keep on unit testing in the future :)
            today_mock.return_value = datetime.date(2010, 1, 1)
    
            # A future date should work
            validators.validate_future_date(datetime.date(2010, 1, 2))
    
            # The mocked today's date should fail
            with self.assertRaises(ValidationError) as e:
                validators.validate_future_date(datetime.date(2010, 1, 1))
            self.assertEquals([u'Date should be in the future.'], e.exception.messages)
    
            # Date in the past should also fail
            with self.assertRaises(ValidationError) as e:
                validators.validate_future_date(datetime.date(2009, 12, 31))
            self.assertEquals([u'Date should be in the future.'], e.exception.messages)
    

The final implementation looks like this:

    
    
    from django.utils.translation import ugettext_lazy as _
    from django.core.exceptions import ValidationError
    
    from utils import date
    
    def validate_future_date(value):
        if value <= date.today():
            raise ValidationError(_('Date should be in the future.'))
    

Hope this helps

