This tutorial will be the first in a small series about writing unit tests in Python, the reason it’s being split is mostly because I am learning more about it as I write! The examples below are written to Python 2.7 though I will cover 3 in the future to highlight their differences. I will also cover some of the excellent libraries available as well as their short comings.
As is the case with every language, good unit tests are an important factor when writing maintainable clean code. Python, like most languages, provides a straightforward way to achieve this. Consider the following simple functions.
def add(num1, num2): | |
return num1 + num2 | |
def subtract(num1, num2): | |
return num1 – num2 | |
def multiply(num1, num2): | |
return num1 * num2 |
Nothing too unusual, just a very basic calculator that we want to write a unit test for to make sure it works as we expect. So here’s our test.
import unittest | |
import simple_calc as sc | |
class SimpleCalcTest(unittest.TestCase): | |
def test_add(self): | |
result = sc.add(1, 2) | |
self.assertEqual(result, 3) | |
def test_subtract(self): | |
result = sc.subtract(2, 1) | |
self.assertEqual(result, 1) | |
def test_multiply(self): | |
result = sc.multiply(2, 2) | |
self.assertEqual(result, 4) | |
if __name__ == '__main__': | |
unittest.main() |
Let’s look at the specific components;
import unittest
The unittest library, part of the standard lib, provides everything we need for our simple test.
class SimpleCalcTest(unittest.TestCase):
Subclassing TestCase means that the test runner understands what our test class is. This is because TestCase provides an interface the test runner can work with.
def test_add(self):
The naming convention, “test_”, is important because this signifies to the test runner this is a test to be run!
self.assertEqual(result, 3)
The whole point of our test is to guarantee the results are what we expect, in this case 1 +2 = 3, so we use an assertion. In this case assertEqual, does what we need but there’s also assertTrue, assertIn, assertIs and many alternatives, all of which provide a way to validate your results.
Here’s the output of running simple_calc_test.py:
... ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK Process finished with exit code 0
Not very interesting but at least we know our functions work! Now let’s see what the result is when we make a modification to simple_calc.py so that the add function no longer behaves as expected.
return num1 + num2 + 3
Our test expects a result of 3 but, now it’s going to get 6 back instead.
F.. ====================================================================== FAIL: test_add (__main__.SimpleCalcTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "simple_calc_test.py", line 9, in test_add self.assertEqual(result, 3) AssertionError: 6 != 3 ---------------------------------------------------------------------- Ran 3 tests in 0.001s FAILED (failures=1)
Whoops! Pretty clear that we’ve made a mistake!
This is a very simplistic example, and in reality it’s not very often you will encounter this situation. In reality your functions will call over functions to achieve their intended purpose and at that point you will need to use some kind of mocking framework, but more on that in part 2!
3 thoughts on “Unit Testing with Python (Part 1) – A Simple Test”