Home > Net >  How can I unit test a function that is called by another function
How can I unit test a function that is called by another function

Time:06-24

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}")
  • Related