Chapter 10: Debugging and testing

Debugging modular programs

face Josiah Wang

Writing highly abstracted and modular programs actually helps make it easier for you to debug your program.

In addition to being self-explanatory, your functions also act as natural ‘breakpoints’ for you to debug your codes.

If you have structured your program well, then you will find it easier to debug as your program is mainly written in terms of ‘inputs’ and ‘outputs’ to/from function calls.

So, if your program is not working as expected, you can start by first figuring out whether the problem is with your input to a function or with your output returned by a function.

For example, you might find that the value of average_dist is not as expected.

1
2
3
4
5
...
dist1 = euclidean_distance(x1_top, y1_left, x1_bottom, y1_right)
dist2 = euclidean_distance(x2_top, y2_left, x2_bottom, y2_right)
average_dist = dist1 + dist2 / 2
print(average_dist)

So you can first check whether dist1 and dist2 is correct.

1
2
3
4
5
6
7
...
dist1 = euclidean_distance(x1_top, y1_left, x1_bottom, y1_right)
dist2 = euclidean_distance(x2_top, y2_left, x2_bottom, y2_right)
print(dist1)
print(dist2)
average_dist = dist1 + dist2 / 2
print(average_dist)

If both dist1 and dist2 are correct, it means that there is something wrong with how you subsequently used these values, i.e. at average_dist = dist1 + dist2 / 2. Indeed, in this example, it should be average_dist = (dist1+dist2) / 2 because of operator precedence.

Otherwise, you should then check whether it is the input to the function that is the problem. You might spot in this example that you have provided the wrong input to the last parameter of the function call in Line 2 (should be y1_right and not y2_right).

1
2
3
4
5
...
dist1 = euclidean_distance(x1_top, y1_left, x1_bottom, y2_right)
dist2 = euclidean_distance(x2_top, y2_left, x2_bottom, y2_right)
average_dist = (dist1 + dist2) / 2
print(average_dist)

Another way to check the input is to actually print out values inside the function definition itself, before you do anything else (Lines 4-7 below). You can also print out the return value here (Line 12, before you return the value). This way, you only need to do it only once, rather than for every function call as earlier. Of course, this also depends on which is more convenient for your situation)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import math

def euclidean_distance(x1, y1, x2, y2):
    print(f"x1: {}")
    print(f"y1: {}")
    print(f"x2: {}")
    print(f"y2: {}")
    dx = x2 - x1
    dy = y2 - y1
    d_squared = dx**2 + dy**2
    dist = math.sqrt(d_squared)
    print(dist)
    return dist

Here, if the input is correct and the output is wrong, then there is clearly a problem with the function itself, so you can focus your attention on debugging inside the function definition.

The more you program and the more you debug, the more experience you will gain, and the more your debugging skills with subsequently improve.