I created a function that returns seconds left until the next occurrence of that time, but I came across a problem writing a unit test for it. How do people test this type of function that has a call to datetime.now()
in it?
Adding another parameter (current_time
) seems wrong just to test it as it changes the initial requirements of the function.
function to test is.
from datetime import datetime, time, timedelta
def get_time_left(target_time):
'''return float of number of seconds left until the target_time'''
if not isinstance( target_time, time ):
raise TypeError("target_time must be datetime.time")
curr_time = datetime.now()
target_datetime = datetime.combine( datetime.today(), target_time )
if curr_time > target_datetime:
target_datetime = curr_time + timedelta(1)
seconds_left = (curr_time - target_datetime).total_seconds()
return seconds_left
The test for it is.
class TestDTime(unittest.TestCase):
def test_time_left(self):
dt_now = datetime.now()
tm_5sec_future = ( dt_now + timedelta(0,5) ).time()
self.assertEqual( dtime.get_time_left(tm_5sec_future), 5.0)
The result is.
======================================================================
FAIL: test_time_left (__main__.TestDTime)
----------------------------------------------------------------------
Traceback (most recent call last):
File "tests/ctatest.py", line 37, in test_time_left
self.assertEqual( dtime.get_time_left(tm_5sec_future), 5.0)
AssertionError: -4.999985 != 5.0
What is best approach to unit testing something like this without adding any args to the function?
You need to use mocking framework to isolate your UT from dependencies so your UT will have a consistence behavior.
Freezegun - is a good mocking library for times which I have been used.
Just install this library: pip install freezegun
The in your UT used the decorator @freeze_time
as the following:
@freeze_time("2018-06-03")
def test_time_left(self):
dt_now = datetime.now()
tm_5sec_future = (dt_now + timedelta(0, 5)).time()
self.assertEqual(dtime.get_time_left(tm_5sec_future), -5.0)