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?
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
@patchdecorator 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
datetimemodule 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.todayin a file calledutils/date.py:Then we create the unittest for this validator in
tests.py:The final implementation looks like this:
Hope this helps