how to measure sleep duration: test_duration_tests
This is V of a program that calculates the difference between a given wake and sleep time.
I want to write a program that makes the tests in test_sleep_duration.py pass without looking at them
RED: make it fail
I close
test_sleep_duration.pythen delete all the text in
sleep_duration.pythe terminal shows AttributeErrorAttributeError: module 'src.sleep_duration' has no attribute 'get_datetime'
GREEN: make it pass
I add a list of Exceptions seen in
test_sleep_duration.py# Exceptions seen # AttributeErrorand the missing name to
sleep_duration.pyget_datetimeNameError: name 'get_datetime' is not definedI add it to the list of Exceptions seen as well
# Exceptions seen # AttributeError # NameErrorand point
get_datetimeto Noneget_datetime = NoneTypeError: 'NoneType' object is not callableanother error for the list of Exceptions seen in
test_sleep_duration.py# Exceptions seen # AttributeError # NameError # TypeErrorI change
get_datetimeto a function to make it callabledef get_datetime(): return NoneTypeError: get_datetime() takes 0 positional arguments but 1 was giventhen I add a name to the function’s definition
def get_datetime(the_input):the terminal shows AssertionError
None != datetime.datetime(2006, 11, 21, 7, 1) None != datetime.datetime(2006, 11, 21, 10, 59) None != datetime.datetime(2006, 11, 21, 13, 28) None != datetime.datetime(2006, 11, 21, 19, 8)which I add to the list of Exceptions seen in
test_sleep_duration.py# Exceptions seen # AttributeError # NameError # TypeError # AssertionErrorI copy the value from the terminal to change None in the return statement
def get_datetime(the_input): return datetime.datetime(2006, 11, 21, 19, 8)NameError: name 'datetime' is not defined. Did you forget to import 'datetime'I add an import statement for the datetime module
import datetime def get_datetime(the_input): ...the terminal shows AssertionError
AssertionError: datetime.datetime(2006, 11, 21, 19, 8) != datetime.datetime(2006, 11, 21, 0, 15)the expected values of the test changed
I change the return statement to see the difference between the input and expected output
def get_datetime(the_input): return the_inputthe terminal shows AssertionError
AssertionError: '2006/11/21 02:58' != datetime.datetime(2006, 11, 21, 2, 58) AssertionError: '2006/11/21 03:14' != datetime.datetime(2006, 11, 21, 3, 14) AssertionError: '2006/11/21 08:30' != datetime.datetime(2006, 11, 21, 8, 30) AssertionError: '2006/11/21 23:41' != datetime.datetime(2006, 11, 21, 23, 41)I need a way to change a string that has a date and time to a datetime.datetime object
I use the datetime.datetime.strptime method to make it happen
def get_datetime(the_input): return datetime.datetime.strptime( argument, '%Y/%m/%d %H:%M' )the terminal shows AttributeError
AttributeError: module 'src.sleep_duration' has no attribute 'duration'I add the name below
get_datetimedurationNameError: name 'duration' is not definedI have done this dance before
I point it to None to define it
duration = NoneTypeError: 'NoneType' object is not callablethen change it to a function to make it callable
def duration(): return NoneTypeError: duration() got an unexpected keyword argument 'sleep_time'I add the name to the function’s definition
def duration(sleep_time):TypeError: duration() got an unexpected keyword argument 'wake_time'which I add to the definition
def duration(sleep_time, wake_time):the terminal shows this AssertionError
AssertionError: ValueError not raisedor
AssertionError: None != '2944049 days, 15:58:00' AssertionError: None != '130331 days, 11:57:00' AssertionError: None != '1829637 days, 5:10:00' AssertionError: None != '2846203 days, 15:15:00'it looks like the
durationfunction has to make a decision based on its inputsI change the return statement to raise ValueError with the inputs or return the inputs to see the difference between them and the expected output
def duration(sleep_time, wake_time): raise ValueError(sleep_time, wake_time)When I raise ValueError in
durationthe terminal shows AssertionError because the message in the ValueError does not match the expectation of the testAssertionError: "wake_time: "3133/04/18 05:11" is earlier than sleep_time: "6999/09/22 05:07"" does not match "('6999/09/22 05:07', '3133/04/18 05:11')" AssertionError: "wake_time: "5856/04/20 15:58" is earlier than sleep_time: "7186/01/12 06:39"" does not match "('7186/01/12 06:39', '5856/04/20 15:58')" AssertionError: "wake_time: "5602/08/29 05:06" is earlier than sleep_time: "8373/05/08 05:29"" does not match "('8373/05/08 05:29', '5602/08/29 05:06')" AssertionError: "wake_time: "7413/05/24 15:04" is earlier than sleep_time: "8720/08/18 01:02"" does not match ""this tells me that the test expects a message with the ValueError, or I get ValueError that looks like this
ValueError: ('0887/08/27 17:21', '5668/08/15 20:16') ValueError: ('2880/08/20 10:10', '9134/08/22 20:28') ValueError: ('6471/03/10 05:04', '7883/06/01 02:38') ValueError: ('7370/08/12 21:34', '7937/03/27 01:58')which does not tell me anything so I comment it out to get the other message I got with the AssertionError, I can raise ValueError again or try to return the inputs
When I get the error with the message about
wake_timebeing earlier thansleep_time, I copy it from the terminal to change the message of the ValueErrordef duration(wake_time, sleep_time): raise ValueError( "wake_time: "7413/05/24 15:04" is earlier than sleep_time: "8720/08/18 01:02"" does not match "" )the terminal shows SyntaxError
SyntaxError: invalid syntax. Perhaps you forgot a comma?or this message
SyntaxError: leading zeros in decimal integer literals are not first_inputermitted; use an 0o prefix for octal integersPython does not know where the string ends or begins because the message has double quotes inside double quotes. I add the error to the list of Exceptions
# Exceptions seen # AttributeError # NameError # TypeError # SyntaxErrorthen change the outer double quotes to single quotes
def duration(wake_time, sleep_time): raise ValueError( 'wake_time: "7413/05/24 15:04"' ' is earlier than ' 'sleep_time: "8720/08/18 01:02"' )the terminal shows AssertionError because the timestamps in the ValueError message are not the same
AssertionError: "wake_time: "1184/07/11 12:07" is earlier than sleep_time: "3059/12/16 04:30"" does not match "wake_time: "0615/04/17 08:51" is earlier than sleep_time: "6631/03/18 20:25"" AssertionError: "wake_time: "2476/05/07 19:46" is earlier than sleep_time: "9204/03/10 20:53"" does not match "wake_time: "0615/04/17 08:51" is earlier than sleep_time: "6631/03/18 20:25"" AssertionError: "wake_time: "3208/04/09 09:10" is earlier than sleep_time: "3957/12/23 22:44"" does not match "wake_time: "0615/04/17 08:51" is earlier than sleep_time: "6631/03/18 20:25"" AssertionError: "wake_time: "7169/09/04 15:18" is earlier than sleep_time: "9367/03/02 03:17"" does not match "wake_time: "0615/04/17 08:51" is earlier than sleep_time: "6631/03/18 20:25""or ValueError
ValueError: wake_time: "7413/05/24 15:04" is earlier than sleep_time: "8720/08/18 01:02"which tells me nothing, so I return
sleep_timeandwake_timedef duration(sleep_time, wake_time): return (sleep_time, wake_time) # raise ValueError( # 'wake_time: "7413/05/24 15:04"' # ' is earlier than ' # 'sleep_time: "8720/08/18 01:02"' # )the terminal shows the AssertionError I got before. I keep switching between the return statement and
raise ValueErroruntil I get the AssertionError that the ValueError messages do not matchI interpolate
wake_timeandsleep_timein the messagedef duration(wake_time, sleep_time): # return (sleep_time, wake_time) raise ValueError( f'wake_time: "{wake_time}"' ' is earlier than ' f'sleep_time: "{sleep_time}"' )the terminal shows ValueError
ValueError: wake_time: "9251/06/04 01:20" is earlier than sleep_time: "1034/03/24 22:35" ValueError: wake_time: "2669/08/09 17:30" is earlier than sleep_time: "2520/01/27 06:40" ValueError: wake_time: "3201/08/13 15:20" is earlier than sleep_time: "1074/03/31 16:44" ValueError: wake_time: "9810/07/30 04:29" is earlier than sleep_time: "9792/03/04 12:44"this is not right, the timestamps for
wake_timeare not earlier thansleep_time. Thedurationfunction needs a condition to make sure it raises ValueError only whenwake_timeis earlier thansleep_time. I add the error to the list of Exceptions seen intest_sleep_duration.py# Exceptions seen # AttributeError # NameError # TypeError # SyntaxError # ValueErrorthen add a condition based on the message from the ValueError
def duration(sleep_time, wake_time): if wake_time < sleep_time: raise ValueError( f'wake_time: "{wake_time}"' ' is earlier than ' f'sleep_time: "{sleep_time}"' ) else: return (sleep_time, wake_time)the terminal shows AssertionError
AssertionError: ('0435/03/20 02:03', '0711/05/03 10:35') != '100850 days, 8:32:00' AssertionError: ('2544/12/29 13:05', '4351/03/05 09:47') != '659692 days, 20:42:00' AssertionError: ('1583/06/02 07:48', '3962/03/06 17:07') != '868824 days, 9:19:00' AssertionError: ('1820/06/12 16:07', '8786/05/18 04:27') != '2544253 days, 12:20:00'it looks like the test expects the difference between the timestamps
I return the difference between
wake_timeandsleep_timedef duration(sleep_time, wake_time): if wake_time < sleep_time: raise ValueError( f'wake_time: "{wake_time}"' ' is earlier than ' f'sleep_time: "{sleep_time}"' ) else: return (sleep_time - wake_time)TypeError: unsupported operand type(s) for -: 'str' and 'str'I still cannot subtract one string from another
I change the return statement back, then add calls to
get_datetimebecause I can do arithmetic with datetime.datetime objectsdef duration(sleep_time, wake_time): if wake_time < sleep_time: raise ValueError( f'wake_time: "{wake_time}"' ' is earlier than ' f'sleep_time: "{sleep_time}"' ) else: return ( get_datetime(sleep_time) - get_datetime(wake_time) )the terminal shows AssertionError
AssertionError: datetime.timedelta(days=-43256, seconds=82860) != '43255 days, 0:59:00' AssertionError: datetime.timedelta(days=-28643, seconds=68100) != '28642 days, 5:05:00' AssertionError: datetime.timedelta(days=-744003, seconds=22500) != '744002 days, 17:45:00' AssertionError: datetime.timedelta(days=-1226280, seconds=76800) != '1226279 days, 2:40:00'the test expects a string and the function returns a datetime.timedelta object. The values for days are also negative and the test expects positive numbers for the days, I did something wrong
I use
str()to match the format of the expectationdef duration(sleep_time, wake_time): if wake_time < sleep_time: raise ValueError( f'wake_time: "{wake_time}"' ' is earlier than ' f'sleep_time: "{sleep_time}"' ) else: return str( get_datetime(sleep_time) - get_datetime(wake_time) )the terminal shows AssertionError
AssertionError: '-2681410 days, 19:19:00' != '2681409 days, 4:41:00' AssertionError: '-1492190 days, 13:23:00' != '1492189 days, 10:37:00' AssertionError: '-398812 days, 16:44:00' != '398811 days, 7:16:00' AssertionError: '-1209690 days, 0:49:00' != '1209689 days, 23:11:00'the
durationfunction returns negative timestamps but the test expects positive timestamps, and the negative days all look like they are one number less than the expectationI switch
wake_timeandsleep_timein the return statementdef duration(sleep_time, wake_time): if wake_time < sleep_time: raise ValueError( f'wake_time: "{wake_time}"' ' is earlier than ' f'sleep_time: "{sleep_time}"' ) else: return str( get_datetime(wake_time) - get_datetime(sleep_time) )the test passes. YES!!
REFACTOR: make it better
I change the name of the positional argument in
get_datetimeto be clearerdef get_datetime(timestamp): return datetime.datetime.strptime( timestamp, '%Y/%m/%d %H:%M' )the terminal shows all tests are still passing
then I remove the list of Exceptions seen because it was just for me
review
The challenge was to write a program that makes the tests in test_sleep_duration.py pass without looking at them. I wrote something that returns the difference between a given wake_time and sleep_time by following these Exceptions from the terminal