0

I want to test a function that calls another function that only asks for input and returns True or False.

I have a function that deletes a booking from a CSV file like so:

    def delete_booking(self, booking_id):
        if Utility().confirm_choice():
            all_bookings_df = pd.read_csv(self.filepath)
            all_bookings_df.set_index("booking_id", inplace=True)
            all_bookings_df.drop([booking_id], inplace=True)
            all_bookings_df.to_csv(self.filepath)
            print("Booking deleted")
        else:
            print("Booking not deleted")

And the confirm_choice function looks like this:

    def confirm_choice(self):
        print("")
        while True:
            confirm = input("Are you sure? (Y/N): ").lower()
            if confirm == "y":
                return True
            elif confirm == "n":
                return False
            else:
                print("Invalid input, try again")

Now I have written a correct and successful test for the confirm_choice function which looks like this:

    def test_confirm_choice(self):
        self.test_init()
        with unittest.mock.patch('builtins.input', return_value='y'):
            self.assertEqual(self.utility.confirm_choice(), True)
        with unittest.mock.patch('builtins.input', return_value='n'):
            self.assertEqual(self.utility.confirm_choice(), False)

But I cannot for the life of me figure out how to enter the confirm_choice function and pass an input there, should I maybe decouple the functions?

This test function just prompts me for an input, but is successful when I input manually:

    def test_delete_booking(self):
        init = Booking()

        with unittest.mock.patch("builtins.input", return_value="y"):
            booking_numbers_for_room = init.get_all_bookings_room(1)
            init.delete_booking(int(booking_numbers_for_room[0].id))
            self.assertEqual(len(init.get_all_bookings_room(1)),
                            len(booking_numbers_for_room) - 1)
tanjavaner
  • 40
  • 6

1 Answers1

0

You can achieve this by replacing sys.stdin with io.StringIO. A context manager helps with this. Python has something similar already in contextlib called redirect_stdout. You can create a similar one

import io
import contextlib

class redirect_stdin(contextlib._RedirectStream):
    _stream = 'stdin'
    
    def __init__(self, value: str):
        stream = io.StringIO(value)
        super().__init__(stream)

# Test func
def func():
    line1 = input()
    line2 = input()
    return f'{line1} {line2}'

# In tests
with redirect_stdin('hello\nworld'):
    result = func()
    print(result)
    
Steven Summers
  • 5,079
  • 2
  • 20
  • 31