Lesson 10
I am Your Father
Chapter 8: Unit testing
Test fixtures
For our next discussion, let’s consider testing for our racing car game. If you recall, back in Lesson 9, I added Wheel
s to our Car
. To test our Car
, we need to first create Wheel
s for the Car
instance.
Below are some example test cases. I am testing the accelerate()
and decelerate()
methods of Car
.
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 26 27 28 |
|
Obviously, this code is pretty repetitive. You have to create new Wheel
instances and then a new Car
instance from the wheels, and then run different tests on the same Car
instance.
You could just create the Car
instance once, and reuse it for both tests. But then you will have to remember to ‘reset’ the properties of the Car
after each test (the speed of the car should be 0
, rotation 0
etc.) Remember that each unit test should be independent of each other - we don’t want side effects from any previous tests!
You can possibly have a separate, non-test function to create the Car
instance, but you will still have to call it from your test function each time.
Test fixtures
What if you can automate the process of creating a new, fresh, Car
instance every time you call a test function? You might also want to clean up everything once you are done. For example, you might want to delete some temporary files you created or generate some reports.
In software testing, such setups are called test fixtures. You put all related tests into the same class. All tests will share the same ‘setting up’ process. The ‘setting up’ process will run before each test is run. Similarly, a ‘clean up’ process will happen after each test has been completed.
For our example, you can subclass from unittest.TestCase
and turn the above test functions into methods. You then create your Car
instance inside a setUp()
method (Line 6). You can optionally also have a tearDown()
method (Line 14) that runs after each test is complete. Note that the module is based on a Java library, which is why the method names follow Java’s camelCase
convention instead of Python’s recommended lower_case
convention.
I have also included some print()
statements in each method so that you can convince yourself that the methods have been invoked! Save this file as test_car.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 26 27 28 29 |
|
You can download car.py
and save it to the same directory if you want to run this piece of code. Feel free to mess with the code if you want to see failed tests!
If you run the test, you can see that setUp()
is run before each test, and tearDown()
is run after each test. The .
is printed by the test runner to indicate a successful test (remember ..
from earlier?)
user@MACHINE:~$ python3 -m unittest test_car
Setting up
Test accelerate
Tearing down
.Setting up
Test decelerate
Tearing down
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
user@MACHINE:~$
Hopefully this gives you a basic glimpse of what you can do with test fixtures, especially once you start writing more complex test cases.