This is an archived version of the course. Please find the latest version of the course on the main webpage.

Chapter 9: Testing and debugging

Test driven development example

face Josiah Wang

Let us examine TDD with an example. Let’s go back to our old example of implementing a Euclidean distance function.

1
2
def euclidean_distance(x1, y1, x2, y2):
    pass

Now, we can start by writing test codes for this functions. In TDD style, we write multiple test cases for each function. For each test, you give the function some input(s), and manually work out what the expected output should be. We cannot write tests for all possible input though, so we usually focus on testing edge cases. You just have to be creative here, thinking of all possible cases that could go wrong. You should also consider cases where the input is invalid. How would you expect your function to behave? This will also help you understand the behaviour of your functions better.

It is convention to prefix test_ to your test cases. Give a semantically meaningful and purposely name, so that you know what you are testing.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
def test_positive_input():
    dist = euclidean_distance(3, 2, 5, 6)
    expected_answer = 2.828427
    epsilon = 0.000001
    assert abs(dist - expected_answer) < epsilon

def test_negative_input():
    dist = euclidean_distance(-3, -2, 5, 6)
    expected_answer = 10
    epsilon = 0.000001
    assert abs(dist - expected_answer) < epsilon

def test_zero_input():
    dist = euclidean_distance(0, 0, 0, 0)
    expected_answer = 0
    epsilon = 0.000001
    assert abs(dist - expected_answer) < epsilon

...

This type of testing is called unit testing, which tests the correctness of a single unit of your code (a function is a natural unit), independent of everything else in your code.

Once you have implemented the unit tests, then you can start implementing your function. In the beginning, you will find all your tests have failed , i.e. in a ‘red’ state. Your aim is to get all your test cases to a ‘green’ state (pass all tests) as soon as possible! Then you can consider your implementation “stable”, and work on improving/refactoring your code if desired!

We won’t have any exercises for this, but I hope you will be able to think in terms of TDD when trying to implement your programs from now on. As well as the straightforward cases, think ahead about all the edge cases and invalid inputs that might break your function, before you start implementing your functions.