I am trying to unit test my program and have decided to unit test the withdraw_cash function. However, it is called by the bank_atm function. I have never called a function that is dependent on another function and am confused about how to do this. Would i use mock and patch to do this?
The tests would be: check whether entering valid amount check whether amount is less than balance
user = {
'pin': 1234,
'balance': 100
}
def withdraw_cash():
while True:
try:
amount = int(input("Enter the amount of money you want to withdraw: "))
except ValueError as v:
print(f"Enter correct amount: ")
else:
if amount > user['balance']:
raise ValueError("You don't have sufficient balance to make this withdrawal")
else:
user['balance'] = user['balance'] - amount
print(f"£{user['balance']}")
return
finally:
print("Program executed")
def bank_atm():
count = 0
to_exit = False
while (count < 3) and (not to_exit):
try:
pin = int(input('Please enter your four digit pin: '))
except ValueError:
print("Please enter correct pin")
count = 1
if pin != user['pin']:
print("Pin does not match.. Try Again")
count = 1
else:
withdraw_cash()
to_exit = True
if count == 3:
print('3 UNSUCCESFUL PIN ATTEMPTS, EXITING')
print('!!!!!YOUR CARD HAS BEEN LOCKED!!!!!')
try:
bank_atm()
except ValueError as v:
print(f"ERROR: {v}")
CodePudding user response:
To expand on the comments. Put UI interactions in separate functions, such as getpin
and getpounds
. They can then be tested separately from the business functions. One can either mix manual input with the unittests or automate them by patching sys.stdin/out. My proof-of-concept experiment, which passes on 3.11.0b3 run from IDLE editor.
import sys
import unittest
from unittest.mock import patch
def getpin(realpin):
return input('Your pin? ') == realpin
class ManualTest(unittest.TestCase):
def test_pin(self):
print('\n(Enter 1234)')
self.assertEqual(getpin('1234'), True)
print('\n(Enter 1)')
self.assertEqual(getpin('1234'), False)
class MockOut:
def write(self, string): pass
# Or 'write = Mock()' to save and check prompts, using mockout.
class MockIn:
def readline(self): # input() uses readline, not read.
return self.line
@patch('sys.stdin', new_callable=MockIn)
@patch('sys.stdout', new_callable=MockOut)
class AutoTest(unittest.TestCase):
def test_pin(self, mockout, mockin):
mockin.line='1234\n' # input removes '\n' if present.
self.assertEqual(getpin('1234'), True)
mockin.line='1233'
self.assertEqual(getpin('1234'), False)
if __name__ == '__main__':
unittest.main(verbosity=2)
CodePudding user response:
I have now refactored my program, by breaking it down into separate functions as you said but when I try and run a test specifically for one function, it runs the whole thing and asks for input from each function. I feel like testing each function individually will have to require specific parameters not dependent on the other function.
user = {
'pin': 1234
}
def withdraw_cash(amount):
balance_account = 100
if amount > balance_account:
raise ValueError("You don't have sufficient balance to make this withdrawal")
else:
new_balance = balance_account - amount
return new_balance
def get_pin():
count = 0
to_exit = False
while (count < 3) and (not to_exit):
try:
pin = int(input('Please enter your four digit pin: '))
except ValueError:
print("Please enter correct pin")
count = 1
if pin != user['pin']:
print("Pin does not match.. Try Again")
count = 1
else:
return get_amount(pin)
#return pin
if count == 3:
a = '3 UNSUCCESFUL PIN ATTEMPTS, EXITING \n !!!!!YOUR CARD HAS BEEN LOCKED!!!!!'
return a
def get_amount(pin):
while True:
try:
amount = int(input("Enter the amount of money you want to withdraw: "))
except ValueError as v:
print(f"Enter correct amount: ")
else:
#print(amount)
return withdraw_cash(amount)
#return amount
try:
get_pin()
except ValueError as v:
print(f"ERROR: {v}")