how to measure sleep duration: test_duration_w_an_earlier_wake_than_sleep_time


This is part 3 of a program that calculates the difference between a given wake and sleep time.


I want to test the duration function with a wake_time that is earlier than the sleep_time

red: make it fail

I add a failing test to test_sleep_duration.py with a while statement to make sure wake_time is always earlier than sleep_time

def test_duration_w_an_earlier_wake_than_sleep_time(self):
    sleep_time = random_timestamp()
    wake_time = random_timestamp()
    while wake_time >= sleep_time:
        wake_time = random_timestamp()

    self.assertEqual(
        sleep_duration.duration(
            sleep_time=sleep_time,
            wake_time=wake_time
        ),
        ''
    )

def test_duration_w_hours_and_minutes(self):
...

and get AssertionError

AssertionError: '-1:00' != ''
AssertionError: '-2:12' != ''
AssertionError: '-8:34' != ''
AssertionError: '-10:57' != ''

green: make it pass

  • The duration function returns a negative timestamp when given an earlier wake_time than sleep_time, which is not a real duration. I want it to return a difference when wake_time is later than or the same as sleep_time and raise ValueError with a message when wake_time is earlier than sleep_time. I change the assertEqual to assertRaisesRegex to catch the Exception when it is raised by the function

    def test_duration_w_an_earlier_wake_than_sleep_time(self):
        sleep_time = random_timestamp()
        wake_time = random_timestamp()
        while wake_time >= sleep_time:
            wake_time = random_timestamp()
    
        with self.assertRaisesRegex(
            ValueError,
            f'wake_time: "{wake_time}"'
            ' is earlier than '
            f'sleep_time: "{sleep_time}"'
        ):
            sleep_duration.duration(
                sleep_time=sleep_time,
                wake_time=wake_time
            )
    

    the terminal shows AssertionError

    AssertionError: ValueError not raised
    

    I add a condition to the duration function to raise the ValueError

    def duration(wake_time=None, sleep_time=None):
        if wake_time < sleep_time:
            raise ValueError(
                f'wake_time: "{wake_time}"'
                ' is earlier than '
                f'sleep_time: "{sleep_time}"'
            )
        else:
            difference_hours = (
                read_timestamp(wake_time)
              - read_timestamp(sleep_time)
            )
            difference_minutes = (
                read_timestamp(wake_time, 1)
              - read_timestamp(sleep_time, 1)
            )
    
            difference = (
                difference_hours*60
              + difference_minutes
            )
    
            duration_hours = difference // 60
            duration_minutes = difference % 60
    
            return (
                f'{duration_hours:02}:'
                f'{duration_minutes:02}'
            )
    

    and the terminal shows a random ValueError for test_duration_w_hours_and_minutes when wake_time is earlier than sleep_time

    ValueError: wake_time: "07:33" is earlier than sleep_time: "08:12"
    ValueError: wake_time: "07:46" is earlier than sleep_time: "14:47"
    ValueError: wake_time: "23:10" is earlier than sleep_time: "23:27"
    ValueError: wake_time: "11:32" is earlier than sleep_time: "13:52"
    
  • I add the error to the list of Exceptions encountered

    # Exceptions Encountered
    # AssertionError
    # TypeError
    # NameError
    # AttributeError
    # ValueError
    
  • then add a while statement to make sure wake_time is not earlier than sleep_time for the difference calculation

    def test_duration_w_hours_and_minutes(self):
        sleep_time = random_timestamp()
        wake_time = random_timestamp()
        while wake_time < sleep_time:
            wake_time = random_timestamp()
    
        difference_hours = (
            int(wake_time.split(':')[0])
          - int(sleep_time.split(':')[0])
        )
        difference_minutes = (
            int(wake_time.split(':')[1])
          - int(sleep_time.split(':')[1])
        )
    
        difference = (
            difference_hours*60
          + difference_minutes
        )
        duration_hours = difference // 60
        duration_minutes = difference % 60
    
        self.assertEqual(
            sleep_duration.duration(
                sleep_time=sleep_time,
                wake_time=wake_time
            ),
            (
                f'{duration_hours:02}:'
                f'{duration_minutes:02}'
            )
        )
    

    and the terminal shows passing tests with no more random failures, green, green, green, green all the way!

refactor: make it better

  • I copy the assertRaisesRegex statement from test_duration_w_an_earlier_wake_than_sleep_time then add it to the while statement to run when wake_time is earlier than sleep_time

    def test_duration_w_hours_and_minutes(self):
        sleep_time = random_timestamp()
        wake_time = random_timestamp()
    
        while wake_time < sleep_time:
            with self.assertRaisesRegex(
                ValueError,
                f'wake_time: "{wake_time}"'
                ' is earlier than '
                f'sleep_time: "{sleep_time}"'
            ):
                sleep_duration.duration(
                    sleep_time=sleep_time,
                    wake_time=wake_time
                )
            wake_time = random_timestamp()
    
        difference_hours = (
            int(wake_time.split(':')[0])
          - int(sleep_time.split(':')[0])
        )
        difference_minutes = (
            int(wake_time.split(':')[1])
          - int(sleep_time.split(':')[1])
        )
    
        difference = (
            difference_hours*60
          + difference_minutes
        )
        duration_hours = difference // 60
        duration_minutes = difference % 60
    
        self.assertEqual(
            sleep_duration.duration(
                sleep_time=sleep_time,
                wake_time=wake_time
            ),
            (
                f'{duration_hours:02}:'
                f'{duration_minutes:02}'
            )
        )
    

    and comment out the condition in duration to make sure the test still works as expected

    def duration(wake_time=None, sleep_time=None):
        # if wake_time < sleep_time:
        #     raise ValueError(
        #         f'wake_time: "{wake_time}"'
        #         ' is earlier than '
        #         f'sleep_time: "{sleep_time}"'
        #     )
        # else:
            difference_hours = (
                read_timestamp(wake_time)
              - read_timestamp(sleep_time)
            )
            difference_minutes = (
                read_timestamp(wake_time, 1)
              - read_timestamp(sleep_time, 1)
            )
    
            difference = (
                difference_hours*60
              + difference_minutes
            )
    
            duration_hours = difference // 60
            duration_minutes = difference % 60
    
            return (
                f'{duration_hours:02}:'
                f'{duration_minutes:02}'
            )
    

    which gives me AssertionError for test_duration_w_an_earlier_wake_than_sleep_time and a random one when wake_time is earlier than sleep_time in test_duration_w_hours_and_minutes

    AssertionError: ValueError not raised
    

    the assertRaisesRegex works as expected. I remove the comments and the terminal shows green again

  • I remove test_duration_w_an_earlier_wake_than_sleep_time because it is now covered by test_duration_w_hours_and_minutes

  • then add an else block for the rest of the code in test_duration_w_hours_and_minutes

    def test_duration_w_hours_and_minutes(self):
        sleep_time = random_timestamp()
        wake_time = random_timestamp()
    
        while wake_time < sleep_time:
            with self.assertRaisesRegex(
                ValueError,
                f'wake_time: "{wake_time}"'
                ' is earlier than '
                f'sleep_time: "{sleep_time}"'
            ):
                sleep_duration.duration(
                    sleep_time=sleep_time,
                    wake_time=wake_time
                )
            wake_time = random_timestamp()
        else:
            difference_hours = (
                int(wake_time.split(':')[0])
              - int(sleep_time.split(':')[0])
            )
            difference_minutes = (
                int(wake_time.split(':')[1])
              - int(sleep_time.split(':')[1])
            )
    
            difference = (
                difference_hours*60
              + difference_minutes
            )
            duration_hours = difference // 60
            duration_minutes = difference % 60
    
            self.assertEqual(
                sleep_duration.duration(
                    sleep_time=sleep_time,
                    wake_time=wake_time
                ),
                (
                    f'{duration_hours:02}:'
                    f'{duration_minutes:02}'
                )
            )
    

    still green

  • I add a static method to calculate the difference between wake_time and sleep_time

    @staticmethod
    def get_difference(
        wake_time=None, sleep_time=None
    ):
        difference_hours = (
            int(wake_time.split(':')[0])
          - int(sleep_time.split(':')[0])
        )
        difference_minutes = (
            int(wake_time.split(':')[1])
          - int(sleep_time.split(':')[1])
        )
    
        difference = (
            difference_hours*60
            + difference_minutes
        )
        duration_hours = difference // 60
        duration_minutes = difference % 60
    
        return (
            f'{duration_hours:02}:'
            f'{duration_minutes:02}'
        )
    
    def test_duration_w_hours_and_minutes(self):
    ...
    

    then call it in test_duration_w_hours_and_minutes to change the expectation

    def test_duration_w_hours_and_minutes(self):
        sleep_time = random_timestamp()
        wake_time = random_timestamp()
    
        while wake_time < sleep_time:
            with self.assertRaisesRegex(
                ValueError,
                f'wake_time: "{wake_time}"'
                ' is earlier than '
                f'sleep_time: "{sleep_time}"'
            ):
                sleep_duration.duration(
                    sleep_time=sleep_time,
                    wake_time=wake_time
                )
            wake_time = random_timestamp()
        else:
            self.assertEqual(
                sleep_duration.duration(
                    sleep_time=sleep_time,
                    wake_time=wake_time
                ),
                self.get_difference(
                    wake_time=wake_time,
                    sleep_time=sleep_time
                )
            )
    

    green again

  • I also add an assert method for the assertRaisesRegex block

    def assertWakeTimeEarlier(
        self, wake_time=None, sleep_time=None
    ):
        with self.assertRaisesRegex(
            ValueError,
            f'wake_time: "{wake_time}"'
            ' is earlier than '
            f'sleep_time: "{sleep_time}"'
        ):
            sleep_duration.duration(
                sleep_time=sleep_time,
                wake_time=wake_time
            )
    
    @staticmethod
    def get_difference(
    ...
    

    and call it in test_duration_w_hours_and_minutes

    def test_duration_w_hours_and_minutes(self):
        sleep_time = random_timestamp()
        wake_time = random_timestamp()
    
        while wake_time < sleep_time:
            self.assertWakeTimeEarlier(
                wake_time=wake_time,
                sleep_time=sleep_time
            )
            wake_time = random_timestamp()
        else:
            self.assertEqual(
                sleep_duration.duration(
                    sleep_time=sleep_time,
                    wake_time=wake_time
                ),
                self.get_difference(
                    wake_time=wake_time,
                    sleep_time=sleep_time
                )
            )
    

    all tests are still passing! Fantastic!

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

Would you like to test duration with timestamps that have dates?


how to measure sleep duration: tests and solution