Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: How best to test functions which use date.today

25 views
Skip to first unread message

Lie Ryan

unread,
Feb 28, 2009, 12:54:05 PM2/28/09
to pytho...@python.org, python-list
Yuan HOng wrote:
> HI,
>
> In my project I have several date related methods which I want tested for
> correctness. The functions use date.today() in several places. Since this
> could change every time I run the test, I hope to find someway to fake a
> date.today.
>
> For illustration lets say I have a function:
>
>
> from datetime import date
> def today_is_2009():
> return date.today().year == 2009
>
> To test this I would like to write test function like:
>
> def test_today_is_2009():
> set_today(date(2008, 12, 31))
> assert today_is_2009() == False
> set_today(date(2009,1,1))
> assert today_is_2009() == True
>
> The first approach of achieving this purpose is to monkey patch the
> date.today like:
>
> date.today = mytoday
>
> But this fails with:
>
> TypeError: can't set attributes of built-in/extension type 'datetime.date'

This is because today is an attribute. In python, we can override
attribute access to become a function call. I don't have python right
now, but try this:

del date.today
date.today = mytoday

> A second possibility would be to change the system date (I am running
> Linux). However the standard Python module doesn't provide a method for this
> purpose. I could use os.system to issue a date command. But I am not very
> comfortable with this since changing the system time could break something
> undesirably. Also I will then have to have root privilege to run my test.
> Besides, I will have to stop the ntp daemon so it will not inadvertently
> correct the system clock during the test period.
>
> Is there any suggestion from the community on how best to test such
> functions?

It is a very bad idea to change the system date.

Christian Heimes

unread,
Feb 28, 2009, 1:39:35 PM2/28/09
to pytho...@python.org
Lie Ryan wrote:
>> But this fails with:
>>
>> TypeError: can't set attributes of built-in/extension type
>> 'datetime.date'
>
> This is because today is an attribute. In python, we can override
> attribute access to become a function call. I don't have python right
> now, but try this:
>
> del date.today
> date.today = mytoday

It won't work. The datetime module is written in C. You can't modify a C
extension.

Christian

rdmu...@bitdance.com

unread,
Feb 28, 2009, 3:34:35 PM2/28/09
to pytho...@python.org

Hmm. Given that, Lie, maybe what you need to do is modify your code
so that you call your own special purpose function to get 'today',
and replace _that_ for testing, using datetime's today for production.

--RDM

Gabriel Genellina

unread,
Feb 28, 2009, 3:38:01 PM2/28/09
to pytho...@python.org
En Sat, 28 Feb 2009 15:35:47 -0200, Yuan HOng <hongyu...@gmail.com>
escribió:

> In my project I have several date related methods which I want tested for
> correctness. The functions use date.today() in several places. Since this
> could change every time I run the test, I hope to find someway to fake a
> date.today.
>
> For illustration lets say I have a function:
>
>
> from datetime import date
> def today_is_2009():
> return date.today().year == 2009
>
> To test this I would like to write test function like:
>
> def test_today_is_2009():
> set_today(date(2008, 12, 31))
> assert today_is_2009() == False
> set_today(date(2009,1,1))
> assert today_is_2009() == True
>

Instead of trying to inject a fake date, you could rewrite the function to
take a date argument:

def today_is_2009(today=None):
if today is None:
today = date.today()
return today.year == 2009

Then, tests should pass a known date. This approach has a drawback -- you
don't test the case when no argument is given.

Another way is to use a fake date class, or a fake datetime module. Google
"python mock object"

--
Gabriel Genellina

Scott David Daniels

unread,
Feb 28, 2009, 4:01:32 PM2/28/09
to
Lie Ryan wrote:

> Yuan HOng wrote:
>> In my project I have several date related methods which I want tested for
>> correctness. The functions use date.today() in several places. Since this
>> could change every time I run the test, I hope to find someway to fake a
>> date.today.
>> For illustration lets say I have a function:
>>
>> from datetime import date
>> def today_is_2009():
>> return date.today().year == 2009
>>
>> To test this I would like to write test function like:
>>
>> def test_today_is_2009():
>> set_today(date(2008, 12, 31))
>> assert today_is_2009() == False
>> set_today(date(2009,1,1))
>> assert today_is_2009() == True
Try something like this:
import module_to_test as sut # "sut" -> system under test
from datetime import date

class FakeDate(object):
def __init__(self, value):
self._result = value
def today(self):
return self._result

def test_today_is_2009_too_old():
temp, sut.date = sut.date, FakeDate(date(2008, 12, 31))
try:
assert not sut.today_is_2009()
finally:
sut.date = temp

def test_today_is_2009_too_young():
temp, sut.date = sut.date, FakeDate(date(2010, 1, 1))
try:
assert not sut.today_is_2009()
finally:
sut.date = temp

def test_today_is_2009_just_right():
temp, sut.date = sut.date, FakeDate(date(2009, 1, 1))
try:
assert not sut.today_is_2009()
finally:
sut.date = temp


Note: each test should test 1 thing.

--Scott David Daniels
Scott....@Acm.Org

Ed Singleton

unread,
Mar 3, 2009, 11:41:36 AM3/3/09
to
On Feb 28, 5:54 pm, Lie Ryan <lie.1...@gmail.com> wrote:
> Yuan HOng wrote:
> > HI,
>
> > In my project I have several date related methods which I want tested for
> > correctness. The functions use date.today() in several places. Since this
> > could change every time I run the test, I hope to find someway to fake a
> > date.today.
>
> > For illustration lets say I have a function:
>
> > from datetime import date
> > def today_is_2009():
> >     return date.today().year == 2009
>
> > To test this I would like to write test function like:
>
> > def test_today_is_2009():
> >     set_today(date(2008, 12, 31))
> >     assert today_is_2009() == False
> >     set_today(date(2009,1,1))
> >     assert today_is_2009() == True

Although you can't override today, you should be able to do something
along the lines of:

class MyDate(object):
def __init__(self, today):
self.today = today

my_date = MyDate(date(2009, 11, 12))

date = my_date

This assumes you aren't using anything else from date. If you are
you'll either have to add that to MyDate or use a proper Mock Object.

Ed

0 new messages