Chapter 8: Unit testing

unittest module

face Josiah Wang

Python provides a built-in module called unittest to help with writing and running unit tests. It is inspired by JUnit, which is a unit testing framework based on Java.

The process is best demonstrated directly with an example.

We will not have any extra exercises for this. So try following along the tutorial, and copying and pasting the codes below and running them yourselves.

Let us use our example of implementing a euclidean_distance() function from way back in Lesson 6.

Assume we have not implemented the function yet. So we will first define a placeholder euclidean_distance() function definition that simply returns 0. Save this file as euclidean.py.

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

In TDD style, you will then write your test cases before implementing the function. Save the following file as test_euclidean.py in the same directory as euclidean.py.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import unittest

from euclidean import euclidean_distance

class EuclideanTest(unittest.TestCase):
    def test_positive_input(self):
        dist = euclidean_distance(3, 2, 5, 6)
        expected_answer = 2.828427
        epsilon = 0.000001
        assert abs(dist - expected_answer) < epsilon

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

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

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

In Line 3, we import our euclidean_distance() function that we wish to test from euclidean.py. This method is a good way to keep your tests separate from your implementation (and this is also how we perform the LabTS tests for your courseworks!)

Now run python3 test_euclidean.py. You should see all three tests being run without any extra effort from you! Congratulations! 🎉

user@MACHINE:~$ python3 test_euclidean.py
Ok
FF.
======================================================================
FAIL: test_negative_input (__main__.EuclideanTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "euclidean_test.py", line 16, in test_negative_input
    assert abs(dist - expected_answer) < epsilon
AssertionError

======================================================================
FAIL: test_positive_input (__main__.EuclideanTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "euclidean_test.py", line 10, in test_positive_input
    assert abs(dist - expected_answer) < epsilon
AssertionError

----------------------------------------------------------------------
Ran 3 tests in 0.003s

FAILED (failures=2)
user@MACHINE:~$

The FF. near the beginning of the output shows that the first two tests Failed, while the final test passed (with a .). It will then show you details about the tests that failed. The order of the test is not necessarily how you ordered them in your code.

If you passed all tests, then it should show ...

The other character that might pop up is E, which stands for an Error that is not an AssertionError. This indicates that some other exception was raised while running the test.

If you pass -v (‘verbose’) as a command line argument, you can get a more detailed description of the test cases instead of just FF..

user@MACHINE:~$ python3 test_euclidean.py -v
test_negative_input (test_euclidean.EuclideanTest) ... FAIL
test_positive_input (test_euclidean.EuclideanTest) ... FAIL
test_zero_input (test_euclidean.EuclideanTest) ... ok

======================================================================
FAIL: test_negative_input (__main__.EuclideanTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "euclidean_test.py", line 16, in test_negative_input
    assert abs(dist - expected_answer) < epsilon
AssertionError
...