how to measure sleep duration: test_duration_tests¶
This is part 5 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.py
then delete all the text in
sleep_duration.py
and the terminal shows AttributeErrorAttributeError: module 'sleep_duration' has no attribute 'get_datetime'
green: make it pass¶
I add a list of Exceptions encountered
# Exceptions Encountered # AttributeError
and the missing name to
sleep_duration.py
get_datetime
the terminal shows NameError
NameError: name 'get_datetime' is not defined
I add it to the list of Exceptions encountered as well
# Exceptions Encountered # AttributeError # NameError
and point
get_datetime
to Noneget_datetime = None
which gives me TypeError
TypeError: 'NoneType' object is not callable
another error for the list of Exceptions encountered
# Exceptions Encountered # AttributeError # NameError # TypeError
I change
get_datetime
to a function to make it callabledef get_datetime(): return None
and the terminal shows another TypeError
TypeError: get_datetime() takes 0 positional arguments but 1 was given
then I add a name to the function’s signature
def get_datetime(argument):
and get 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 encountered
# Exceptions Encountered # AttributeError # NameError # TypeError # AssertionError
I copy the value from the terminal to change None in the return statement
def get_datetime(argument): return datetime.datetime(2006, 11, 21, 19, 8)
and the terminal shows NameError
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(argument): ...
and get 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(argument): return argument
and the 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(argument): return datetime.datetime.strptime( argument, '%Y/%m/%d %H:%M' )
and get AttributeError
AttributeError: module 'sleep_duration' has no attribute 'duration'
I add the name below
get_datetime
duration
and get NameError
NameError: name 'duration' is not defined
I have done this dance before
I point it to None to define it
duration = None
which gives me TypeError
TypeError: 'NoneType' object is not callable
then change it to a function to make it callable
def duration(): return None
and the terminal shows another TypeError
TypeError: duration() got an unexpected keyword argument 'sleep_time'
I add the name to the function’s signature
def duration(sleep_time):
and get TypeError for another keyword argument
TypeError: duration() got an unexpected keyword argument 'wake_time'
which I add to the signature
def duration(sleep_time, wake_time):
the terminal shows this AssertionError
AssertionError: ValueError not raised
or
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
duration
function 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
duration
I get another 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_time
being 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 "" )
and get a SyntaxError with this message
SyntaxError: invalid syntax. Perhaps you forgot a comma?
or this message
SyntaxError: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers
Python 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 Encountered # AttributeError # NameError # TypeError # SyntaxError
then 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"' )
and get another 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_time
andwake_time
def 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"' # )
and get the AssertionError I got before. I keep switching between the return statement and
raise ValueError
until I get the AssertionError that the ValueError messages do not matchI interpolate
wake_time
andsleep_time
in 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}"' )
and get 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_time
are not earlier thansleep_time
. Theduration
function needs a condition to make sure it raises ValueError only whenwake_time
is earlier thansleep_time
. I add the error to the list of Exceptions encountered# Exceptions Encountered # AttributeError # NameError # TypeError # SyntaxError # ValueError
then 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)
and 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_time
andsleep_time
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)
and get TypeError
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_datetime
because 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) )
and 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 add the str constructor to match the format of the expectation
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 str( get_datetime(sleep_time) - get_datetime(wake_time) )
and get another 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
duration
function 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_time
andsleep_time
in 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) )
and the terminal shows passing tests! YES!!
refactor: make it better¶
I change the name of the positional argument in
get_datetime
to be more descriptivedef get_datetime(timestamp): return datetime.datetime.strptime( timestamp, '%Y/%m/%d %H:%M' )
and the terminal shows all tests are still passing
then I remove the list of Exceptions encountered 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