Unit tests with Python
Posted on December 13, 2021 by Adrian Wyssmann ‐ 3 min read
As we know now, we need a unit test framework to do unit testing. If we have a look at Python, it comes with a unittest framework included:
The unittest unit testing framework was originally inspired by JUnit and has a similar flavor as major unit testing frameworks in other languages.
It offers the concepts I discussed here:
- test fixture - well, provide text fixture
- test case - the individual unit of testing
- test suite - a collection of test cases, test suites, or both
- test runner - the component which orchestrates the execution of tests and provides the outcome to the user
Let’s say we have a script example.py
with the following content:
def simpleFunction(x,y):
if (5 <= x and y < 10):
return "do this"
else:
return "do that"
We would create a test case which tests the different path of simpleFunction
. We call the file test_example.py
as follows
from example import *
import unittest
class SimpleFunctionTest(unittest.TestCase):
def test_dothistest(self):
self.assertAlmostEqual(simpleFunction(6,2), 'do this')
self.assertAlmostEqual(simpleFunction(5,9), 'do this')
def test_dothattest(self):
self.assertAlmostEqual(simpleFunction(5,10), 'do that')
self.assertAlmostEqual(simpleFunction(1,9), 'do that')
if __name__ == '__main__':
unittest.main()
Some things which you need to know
- a test class is usually
test_classname.py
- don’t use a.
i.eclassname.test.py
as this will result in an error when executing the tests - it must import
unittest
as well as test object - in this caseexample
- a test classis subclassing
unittest.TestCase
. - the name of the test class does not matter, but the test functions have to be prefixed with
test_
- otherwise the functions will not be considered test cases - call
unittest.main()
when__name__ == '__main__
- this provides a command-line interface to the test script
You can call now the unit tests from the command line - we use -v
to see the name of the test cases:
$ python3 example_test.py -v
test_dothattest (example_test.SimpleFunctionTest) ... ok
test_dothistest (example_test.SimpleFunctionTest) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
If you have multiple files, you can also test discovery, which automatically detects tests.
$ python -m unittest -v
test_dothattest (test_example.SimpleFunctionTest) ... ok
test_dothistest (test_example.SimpleFunctionTest) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Or
python -m unittest discover -v
test_dothattest (example_test.SimpleFunctionTest) ... ok
test_dothistest (example_test.SimpleFunctionTest) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
It also offers setUp()
and tearDown()
methods, which will be executed before and after each test method.
You also can skip tests using the skip()
-decorator or raise skipTest
. Here an example of the official documentation:
class MyTestCase(unittest.TestCase):
@unittest.skip("demonstrating skipping")
def test_nothing(self):
self.fail("shouldn't happen")
@unittest.skipIf(mylib.__version__ < (1, 3),
"not supported in this library version")
def test_format(self):
# Tests that work for only a certain version of the library.
pass
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
def test_windows_support(self):
# windows specific testing code
pass
def test_maybe_skipped(self):
if not external_resource_available():
self.skipTest("external resource not available")
# test code that depends on the external resource
pass
If you want to group test cases together, you can build TestSuites
import unittest
from example_test import *
def suite():
suite = unittest.TestSuite()
suite.addTest(SimpleFunctionTest('test_dothistest'))
suite.addTest(SimpleFunctionTest('test_dothattest'))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
You can run this as follows:
python example_suite_test.py -v
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Another interesting feature is SubTests
, which allows you to distinguish small differences for some parameters, using the same test method.
I recommend to read organize test code to better understand how you should organize your test code.
When writing code, it’s important you understand unit testing and the framework, the language of your choice offers. Whether it’s unittest for Python or JUnit for Java, they support the same important concepts, even so they differ in syntax.