how to measure sleep duration: test_duration_w_hours
This is part 1 of a program that calculates the difference between a given wake and sleep time.
requirements
I open a terminal to run makePythonTdd.sh with
sleep_durationas the name of the project./makePythonTdd.sh sleep_durationAttention
on Windows without Windows Subsystem for Linux use makePythonTdd.ps1 instead of makePythonTdd.sh
./makePythonTdd.ps1 sleep_durationit makes the folders and files that are needed, installs packages, runs the first test, and the terminal shows AssertionError
E AssertionError: True is not false tests/test_sleep_duration.py:7: AssertionErrorI hold ctrl (Windows/Linux) or option/command (MacOS) on the keyboard and use the mouse to click on
tests/test_sleep_duration.py:7to put the cursor on line 7 in the editor-
7 self.assertFalse(False)the test passes
I change the name of the class to match the CapWords format to follow Python convention
4class TestSleepDuration(unittest.TestCase):
RED: make it fail
I change the test to a new failing test
1import unittest 2 3 4class TestSleepDuration(unittest.TestCase): 5 6 def test_duration_w_hours(self): 7 self.assertEqual( 8 9 )TypeError: TestCase.assertEqual() missing 2 required positional arguments: 'first' and 'second'which I add to the list of Exceptions seen in
test_sleep_duration.py# Exceptions seen # AssertionError # TypeError
GREEN: make it pass
I add None as the first and second arguments of the assertion
self.assertEqual( None, None )then change the first argument to the
sleep_durationmoduleself.assertEqual( src.sleep_duration, None )NameError: name 'src' is not definedI add it to the list of Exceptions seen in
test_sleep_duration.py# Exceptions seen # AssertionError # TypeError # NameErrorthen add an import statement at the top of the file for the module
import src.sleep_duration import unittest class TestSleepDuration(unittest.TestCase): ...the terminal shows AssertionError
AssertionError: <module 'src.sleep_duration' from '/workspace[46 chars].py'> != NoneI use something in the
sleep_durationmoduleself.assertEqual( src.sleep_duration.duration, None )the terminal shows AttributeError
AttributeError: module 'src.sleep_duration' has no attribute 'duration'I add the error to the list of Exceptions seen in
test_sleep_duration.py# Exceptions seen # AssertionError # TypeError # NameError # AttributeErrorthen open
sleep_duration.pyin the editor to add the namedurationNameError: name 'duration' is not definedI point it to None to define it
duration = Nonethen add a call to
durationin the testself.assertEqual( src.sleep_duration.duration(), None )TypeError: 'NoneType' object is not callableI make it callable by changing it to a function
def duration(): return NoneI want the
durationfunction to take in awake_timethen add it to the testself.assertEqual( src.sleep_duration.duration( wake_time='08:00' ), None )then get TypeError
TypeError: duration() got an unexpected keyword argument 'wake_time'because the name is not in the function’s definition. I add it with a default value of None
def duration(wake_time=None): return NoneI also want the
durationfunction to take in asleep_timeself.assertEqual( src.sleep_duration.duration( wake_time='08:00', sleep_time='07:00' ), None )TypeError: duration() got an unexpected keyword argument 'sleep_time'because
sleep_timeis not in the function’s definition. I add it with a default value of Nonedef duration(wake_time=None, sleep_time=None): return Nonethen set the expectation of the test to the given inputs
self.assertEqual( src.sleep_duration.duration( wake_time='08:00', sleep_time='07:00' ), ('08:00', '07:00') )the terminal shows AssertionError
AssertionError: None != ('08:00', '07:00')the
durationfunction returns None, I change it to match the expectationdef duration(wake_time=None, sleep_time=None): return ('08:00', '07:00')the test passes
REFACTOR: make it better
I add variables to remove the repetition of the values for
wake_timeandsleep_timedef test_duration_w_hours(self): wake_time = '08:00' sleep_time = '07:00' self.assertEqual( src.sleep_duration.duration( wake_time=wake_time, sleep_time=sleep_time ), (wake_time, sleep_time) )then change
wake_timedef test_duration_w_hours(self): wake_time = '09:00' sleep_time = '07:00'the terminal shows AssertionError
AssertionError: Tuples differ: ('08:00', '07:00') != ('09:00', '07:00')I change
durationto matchdef duration(wake_time=None, sleep_time=None): return ('09:00', '07:00')the test passes
I change
sleep_timedef test_duration_w_hours(self): wake_time = '09:00' sleep_time = '06:00'the terminal shows AssertionError
AssertionError: Tuples differ: ('09:00', '07:00') != ('09:00', '06:00')then change
durationto match the expectationdef duration(wake_time=None, sleep_time=None): return ('09:00', '06:00')the test passes
I do not want to change the values of
wake_timeandsleep_timein the tests every time I have an ideand then change thedurationfunction to match. It would be better to test the function with random numbers. I add an import statement for the random module at the top oftest_sleep_duration.pyimport random import src.sleep_duration import unittestthen add variables for random hours in a day
def test_duration_w_hours(self): wake_hour = random.randint(0, 23) sleep_hour = random.randint(0, 23) wake_time='09:00' sleep_time='06:00' ...random.randint gives me a random number from
0up to and including23for the 24 hours in a dayI interpolate them as hours for
wake_timeandsleep_timedef test_duration_w_hours(self): wake_hour = random.randint(0, 23) sleep_hour = random.randint(0, 23) wake_time=f'{wake_hour:02}:00' sleep_time=f'{sleep_hour:02}:00' ...the
:02tells Python to always show the numbers as 2 digits, if it is less than10it shows have a0in front of it, for example01. The terminal shows AssertionErrorAssertionError: Tuples differ: ('09:00', '06:00') != ('10:00', '02:00') AssertionError: Tuples differ: ('09:00', '06:00') != ('23:00', '00:00') AssertionError: Tuples differ: ('09:00', '06:00') != ('07:00', '03:00') AssertionError: Tuples differ: ('09:00', '06:00') != ('00:00', '22:00')durationstill returns('09:00', '06:00')but the test now uses random timestamps. I change it to return its inputs remember the identity functiondef duration(wake_time=None, sleep_time=None): return (wake_time, sleep_time)the test passes
I change the expectation of the test to
wake_time-sleep_timeself.assertEqual( src.sleep_duration.duration( wake_time=wake_time, sleep_time=sleep_time ), (wake_time-sleep_time) )TypeError: unsupported operand type(s) for -: 'str' and 'str'the timestamps are strings and I cannot subtract one string from another. I undo the change to go back to what was working
self.assertEqual( src.sleep_duration.duration( wake_time=wake_time, sleep_time=sleep_time ), (wake_time, sleep_time) )I want to get the hours part of
wake_timeandsleep_timewhich are the characters before:. I add a call to the help system to see which methods of strings can help me break one apart or get specific parts from itdef test_duration_w_hours(self): self.assertEqual(help(str)) ...the terminal shows Python documentation for strings and I read the descriptions until I see a method that looks like what I am looking for
... | | split(self, /, sep=None, maxsplit=-1) | Return a list of the substrings in the string, | using sep as the separator string. | | sep | The separator used to split the string. | ...I remove
self.assertEqual(help(str))def test_duration_w_hours(self): wake_hour = random.randint(0, 23) sleep_hour = random.randint(0, 23) ...
test_string_splitting
RED: make it fail
I add a failing test for the str.split method to see what it does
def test_string_splitting(self):
self.assertEqual(
'01:23'.split(), None
)
def test_duration_w_hours(self):
...
the terminal shows AssertionError
AssertionError: ['01:23'] != None
GREEN: make it pass
I copy the list from the terminal and paste it in the test to make it pass
self.assertEqual(
'01:23'.split(), ['01:23']
)
the test passes
REFACTOR: make it better
I change the expectation to the hours and minutes as different items
self.assertEqual( '01:23'.split(), ['01', '23'] )the terminal shows AssertionError
AssertionError: Lists differ: ['01:23'] != ['01', '23']the documentation showed that str.split takes in a separator. I want to see what happens when I pass in
':'as the separatorself.assertEqual( '01:23'.split(':'), ['01', '23'] )the test passes which means I know how to get the different parts of
wake_timeandsleep_timeI add calls to the str.split method in
test_duration_w_hoursself.assertEqual( src.sleep_duration.duration( wake_time=wake_time, sleep_time=sleep_time ), ( wake_time.split(':'), sleep_time.split(':') ) )the terminal shows AssertionError
AssertionError: Tuples differ: ('00:00', '10:00') != (['00', '00'], ['10', '00']) AssertionError: Tuples differ: ('23:00', '08:00') != (['23', '00'], ['08', '00']) AssertionError: Tuples differ: ('06:00', '11:00') != (['06', '00'], ['11', '00']) AssertionError: Tuples differ: ('13:00', '13:00') != (['13', '00'], ['13', '00'])the
durationfunction returnswake_timeandsleep_timebut the test expects the result of splitting them. I change it to match the expectationdef duration(wake_time=None, sleep_time=None): return ( wake_time.split(':'), sleep_time.split(':') )the terminal shows green again
I want the hours part of the timestamp string which is the first item from calling str.split. From the chapter on lists I know I can get it by using its index, Python uses zero-based indexing which means the first item has an index of
0and the second an index of1. I add a variable totest_string_splittingdef test_string_splitting(self): split = '01:23'.split(':') self.assertEqual(split, ['01', '23'])the terminal still shows passing tests. I add an assertion for indexing the list
self.assertEqual(split, ['01', '23']) self.assertEqual(split[0], 0)the terminal shows AssertionError because the first item (index 0) from splitting
'01:23'on the separator':'is'01', the hours part of the timestampAssertionError: '01' != 0I change the value in the test to
'01'self.assertEqual(split[0], '01')and it passes
I add another assertion for the minutes
self.assertEqual(split[0], '01') self.assertEqual(split[1], '01')the terminal shows AssertionError
AssertionError: '23' != '01'the second item (index 1) from splitting
'01:23'on the separator':'is'23', the minutes part of the timestamp. I change the'01'to'23'self.assertEqual(split[1], '23')the test passes
I change the expectation of
test_duration_w_hoursto the hours fromwake_timeandsleep_timeself.assertEqual( src.sleep_duration.duration( wake_time=wake_time, sleep_time=sleep_time ), ( wake_time.split(':')[0], sleep_time.split(':')[0] ) )the terminal shows AssertionError
AssertionError: Tuples differ: (['00', '00'], ['19', '00']) != ('00', '19') AssertionError: Tuples differ: (['14', '00'], ['17', '00']) != ('14', '17') AssertionError: Tuples differ: (['05', '00'], ['08', '00']) != ('05', '08') AssertionError: Tuples differ: (['23', '00'], ['04', '00']) != ('23', '04')the
durationfunction returns the result of splitting the timestamps but the test expects the hours, I change it to match the expectationdef duration(wake_time=None, sleep_time=None): return ( wake_time.split(':')[0], sleep_time.split(':')[0] )the test passes
test_converting_strings_to_numbers
The hours part of the timestamp after calling str.split is still a string and I got TypeError when I tried to subtract one from another earlier. I want to see if I can use int() to change a string to a number
I add a new failing test to test numbers that have a
0in front of themdef test_converting_strings_to_numbers(self): self.assertEqual(int('01'), 0) def test_duration_w_hours(self): ...the terminal shows AssertionError
AssertionError: 1 != 0I change the expectation to
1self.assertEqual(int('01'), 1)the test passes
I add another assertion to test a bigger number
self.assertEqual(int('23'), 1)the terminal shows AssertionError
AssertionError: 23 != 1I change the number from
1to23self.assertEqual(int('23'), 23)the terminal shows green again
I add calls to
int()in the expectation oftest_duration_w_hoursself.assertEqual( src.sleep_duration.duration( wake_time=wake_time, sleep_time=sleep_time ), ( int(wake_time.split(':')[0]), int(sleep_time.split(':')[0]) ) )the terminal shows AssertionError
AssertionError: Tuples differ: ('00', '05') != (0, 5) AssertionError: Tuples differ: ('08', '21') != (8, 21) AssertionError: Tuples differ: ('04', '04') != (4, 4) AssertionError: Tuples differ: ('16', '14') != (16, 14)the
durationfunction returns the hours as a string but the test expects them as numbers, I change it to match the expectationdef duration(wake_time=None, sleep_time=None): return ( int(wake_time.split(':')[0]), int(sleep_time.split(':')[0]) )the test passes
I change the expectation in
test_duration_w_hoursto the difference between the hoursself.assertEqual( src.sleep_duration.duration( wake_time=wake_time, sleep_time=sleep_time ), ( int(wake_time.split(':')[0]) -int(sleep_time.split(':')[0]) ) )the terminal shows AssertionError
AssertionError: (7, 23) != -16 AssertionError: (11, 4) != 7 AssertionError: (12, 21) != -9 AssertionError: (14, 2) != 12the
durationfunction returns the hours from the timestamps and the test expects the difference between them. I change thedurationfunction to match the expectationdef duration(wake_time=None, sleep_time=None): return ( int(wake_time.split(':')[0]) - int(sleep_time.split(':')[0]) )the test passes! Celebration Time!!
I add a function to get the hours part of a given timestamp since it is the only part that changes in the solution
def get_hour(timestamp): return int(timestamp.split(':')[0])then call it in
durationdef duration(wake_time=None, sleep_time): return ( get_hour(wake_time) - get_hour(sleep_time) )and the terminal still shows passing tests!
wake_hourandsleep_hourare only used once intest_sleep_duration.py, I can change them with direct calls to random.randintdef test_duration_w_hours(self): wake_time = f'{random.randint(0,23):02}:00' sleep_time = f'{random.randint(0,23):02}:00' ...the terminal still shows passing tests
wake_timeandsleep_timeare defined in the same way, time to make a function that returns a random timestampdef random_timestamp(): return f'{random.randint(0,23):02}:00'and call it in
test_duration_w_hoursdef test_duration_w_hours(self): sleep_time = random_timestamp() wake_time = random_timestamp() ...all tests are still passing! What a beautiful life!!
review
The challenge is to write a program that calculates the difference between a given wake and sleep time. I ran the following tests to get something that comes close to doing it
test_string_splitting where I
used the str.split method I found by calling the help system to split a string on a separator
and indexed the list from the split to get specific items
test_converting_strings_to_numbers with
int()test_duration_w_hours where I
used random.randint to generate random numbers from the 24 hours in a day and how to pass values them in the timestamps
then test that the
durationfunction subtracts the hour forsleep_timefrom the hour forwake_time
I also saw the following Exceptions
Would you like to test duration with hours and minutes?