Amin Etesamian Amin Etesamian - 1 year ago 79
Python Question

Testing a time-dependent method

I have a method that takes a datetime and returns what period of time this datetime belongs to, for example "yesterday" or "a month ago".

from datetime import datetime


def tell_time_ago(basetime):
difference = datetime.now() - basetime

days = difference.days
seconds = difference.seconds
hours = seconds / 3600
minutes = seconds / 60

if days and days == 1:
return 'Yesterday'
elif days and days != 1 and days < 7:
return '%s days ago' % days
elif days and days != 1 and 7 < days < 31:
return 'Within this month'
elif days and days != 1 and 30 < days < 365:
return '%s months ago' % (days / 30)
elif days and days != 1 and 365 <= days < 730:
return 'A year ago'
elif days and days != 1 and days >= 730:
return '%s years ago' % (days / 365)

elif hours and hours == 1:
return 'An hour ago'
elif hours and hours != 1:
return '%s hours ago' % hours

elif minutes and minutes == 1:
return 'A minute ago'
elif minutes and minutes != 1:
return '%s minutes ago' % minutes

elif seconds and seconds == 1:
return 'A second ago'
elif seconds and seconds != 1:
return '%s seconds ago' % seconds

else:
return '0 second ago'


I want to extend this method so I'm going to write tests for it. If want to write a test for this method, should I change the current date of system to a specific date and revert it back to normal every time, so the test won't fail just because the date is changed? For example:

class TestCase(unittest.TestCase):
def test_timedelta(self):
a_year_ago = datetime(2015, 5, 12, 23, 15, 15, 53000)
assert tell_time_ago(a_year_ago) == 'A year ago'


If I run this test two years from now, it will fail. What is the best approach?

Answer Source

In general, I think it's a good idea to make your nontrivial functions and classes as close to mathematical functions (e.g., sin(x)), as possible. Given the same input, a mathematical function gives the same output each time, irrespective of the current date, random choices, and so forth.

  • If your function performs nontrivial logic dependent on the current date or time, pass the current date or time externally to it.

  • If your function performs random choices, pass it a pseudo-random number generator.

So, for example, instead of:

import datetime

def foo():
    ...
    now = datetime.datetime.now()
    ...

foo()

Use

import datetime

def foo(now):
    ...
    ...

foo(datetime.datetime.now())

This makes your nontrivial code consistent across multiple executions.

  1. You can predictably test it.

  2. If it fails in production, it is easier to reconstruct the problem.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download