Debugging with Python - Part 3

Debugging with Python - Part 3

Debugging with Python - Part 3

Refer this article for Debugging with Python - Part 1.
Refer this article for Debugging with Python - Part 2 (Debugging techniques - Part 1).

Debugging techniques - Part 2

5. Exception Handling:
Exception handling is the process used to prevent runtime errors occurring at the execution time, so that these errors wouldn’t cause our program to crash. In python, we can use try and except blocks to help us handle runtime errors and print out useful error messages.

Let’s modify the simple calculator program we used in our type casting article to handle exceptions of user input and division by zero.

while True:
    try:
        number1 = float(input("Enter 1st number: "))
        number2 = float(input("Enter 2nd number: "))

        result = round(number1 / number2, 2)
        
    except ValueError:
        print("Incorrect value, Enter a valid number.")
    except ZeroDivisionError:
        print("Cannot divide by zero")
    else:
        print(f"{number1} / {number2} = {result}")
        break

We have used a while loop to iterate through the program incase an exception is triggered. As we discussed in type casting and debugging - part1 articles, improper string values will raise a ValueError exception and in order to handle it and output an error message we have used the except ValueError statement. We can have multiple except statements to handle various exceptions. Thus, we have used the second except (ZeroDivisionError) statement to handle scenarios where user inputs a 0 for the number2 variable.

We can use else statement to handle scenarios where no exception is occurred. Hence we use it here to output our result and exit the program. Accordingly, If the program gets executed successfully without causing any exception, the break statement inside the else block will terminate the while loop.
Additionally, we may use a finally block to write some code we want to get executed regardless of an exception occurring.


6. Debugger
Using a debugger is a more sophisticated method for identifying logical errors in a program. Debuggers operate by letting us insert breakpoints (i.e. temporary pause of execution) in our code to step through the execution while interactively examining the variables. Generally, IDEs have their own built in debuggers (Ex: PyCharm Debugger, Visual Studio Code Debugger) that we can use to debug our Python code.

Moreover, if we are developing in a server environment with a command line tool, we may use a debugger module provided by the language or a third party. Python provides a built-in debugger module called pdb which we can use in these instances. There are also more advanced and improved versions of pdb debuggers available such as pdb++ and ipdb. This list comprises some more debuggers available for python programs and their platform compatibilities.


7. Unit Tests
Writing unit tests is a proactive way that provides a mechanism to catch errors and ensure that our code is working as expected. Python’s built-in unittest library provides a framework that can be used for creating and running tests for functional components of our program.

Let’s create two files; one to write a function that returns the product of two numbers (multiply.py) and another to write the test cases for that function (test_multiply.py) using Python’s unittest framework.

# multiply.py
def  multiply_numbers(num1, num2):
    return  round(num1  *  num2, 2)
    
# test_multiply.py 
import unittest
import multiply


class TestMultiply(unittest.TestCase):

    def test_multiply_positive_numbers(self):
        self.assertEqual(multiply.multiply_numbers(4, 5), 20)

    def test_multiply_negative_numbers(self):
        self.assertEqual(multiply.multiply_numbers(-6, -3), 18)

    def test_multiply_positive_and_negative_numbers(self):
        self.assertEqual(multiply.multiply_numbers(7, -4), -28)

    def test_multiply_decimals(self):
        self.assertEqual(multiply.multiply_numbers(3.6587, 6.3254), 23.14)


if __name__ == "__main__":
    unittest.main()

We have defined a test case class named TestMultiply which inherits from TestCaseclass. This class contains four test methods to test different inputs. We can include more methods as per our testing requirements. We have used the assertEqual method of the TestCase class to test our multiply_numbers function. We have to pass inputs and expected outputs as method arguments of the assertEqual method. There are more assert methods provided by the TestCase class that we can use within our test methods.


8. Code reviews
Code review or a peer review is a process where someone other than the author of the code examines the code and share their opinion which will be beneficial in identifying errors. Code reviews can be done to identify any type of the three types of errors since we get the opportunity to get a second opinion or a different perspective. In addition to helping catch errors, code reviews can also facilitate knowledge sharing as these help us learn new techniques and best practices from other developers. Moreover, they are a valuable tool for improving code quality and team collaboration.

Comments

Popular posts from this blog

Data Structures - Part 1

OOP with Python - Part 2

OOP with Python - Part 1