This post contains examples of how unittest test fixture functions and methods are written, and in what order they run. It may seem like a long post, but it’s mostly code examples and example output.
I want this to be a useful reference for both the syntax and flow of unittest fixtures.
If I missed something, please comment below, and I’ll update the post.
Intro: Software Test Fixtures
The term test fixtures really means two things.
Test fixtures are the resources and initial conditions that a test needs to operate correctly and independently from other tests.
The phrase has also grown to mean the functions and methods that are used to do that resource and environment handling.
For the rest of this post, I’m really referring to the functions and methods when I say fixtures.
Test fixtures are methods and functions that run before and after a test.
The intent is to provide developers hooks to set up preconditions needed for the test, and cleanup after the test.
In many cases, this will be allocating, opening, or connecting to some resource in the setUp, and deallocating, closing, or disconnecting in the tearDown.
However, that’s just the intent. You can use these really however you want to use them.
One great use for fixtures is to set up structures or variables the same way for all tests.
This is to make sure that tests can run individually as well as a set and in any order.
Common Case Example
The most common fixture methods are setUp and tearDown.
The deserve to be the most common, because they are the ones that allow test independence.
The setUp()
method runs before every test.
The tearDown()
method runs after every test.
Here’s some code.
import unittest
import inspect
class TestLists(unittest.TestCase):
def setUp(self):
self.logPoint()
self.myList = [1, 2, 3, 4]
def test_len(self):
self.logPoint()
self.assertEqual( len(self.myList), 4 )
self.myList.append(-1)
self.assertEqual( len(self.myList), 5 )
def test_min(self):
self.logPoint()
self.assertEqual( min(self.myList) , 1 )
def tearDown(self):
self.logPoint()
def logPoint(self):
currentTest = self.id().split('.')[-1]
callingFunction = inspect.stack()[1][3]
print 'in %s - %s()' % (currentTest, callingFunction)
And here’s the output.
> python -m unittest -q test_lists
in test_len - setUp()
in test_len - test_len()
in test_len - tearDown()
in test_min - setUp()
in test_min - test_min()
in test_min - tearDown()
----------------------------------------------------------------------
Ran 2 tests in 0.032s
OK
Note that the tests are wrapped with setUp()
and tearDown()
just as promised.
Tracing flow with logPoint()
In the code above, I’ve written a utility method logPoint()
to demonstrate the control flow through the test.
I’m sure there’s a cleaner way to do this, but I couldn’t come up with a cleaner method.
I’m not going to describe the inspect module.
However, it is good to note I’m using the id()
method that is part of unittest.TestCase
to get the name of the current test. This is valid during the test method, as well as setUp, tearDown, and any methods called from the test method.
Full Test Fixture Example
Although setUp()
and tearDown()
are the methods that allow us to make sure each test can run independently and in any order, we have other methods available as well.
I think this is a complete list.
setUp()
/ tearDown()` – before and after test methodssetUpClass()
/tearDownClass()
– before and after a class of testssetUpModule()
/tearDownModule()
– before and after a module of tests- Cleanup functions – extra tearDown methods that can be added at runtime to any test method during setUp, or during the test method itself.
Here’s some code with everything but cleanup functions.
import unittest
import inspect
def logPoint(context):
'utility function used for module functions and class methods'
callingFunction = inspect.stack()[1][3]
print 'in %s - %s()' % (context, callingFunction)
def setUpModule():
'called once, before anything else in this module'
logPoint('module %s' % __name__)
def tearDownModule():
'called once, after everything else in this module'
logPoint('module %s' % __name__)
class TestFixtures(unittest.TestCase):
@classmethod
def setUpClass(cls):
'called once, before any tests'
logPoint('class %s' % cls.__name__)
@classmethod
def tearDownClass(cls):
'called once, after all tests, if setUpClass successful'
logPoint('class %s' % cls.__name__)
def setUp(self):
'called multiple times, before every test method'
self.logPoint()
def tearDown(self):
'called multiple times, after every test method'
self.logPoint()
def test_1(self):
'a test'
self.logPoint()
def test_2(self):
'another test'
self.logPoint()
def logPoint(self):
'utility method to trace control flow'
callingFunction = inspect.stack()[1][3]
currentTest = self.id().split('.')[-1]
print 'in %s - %s()' % (currentTest, callingFunction)
Full Test Fixture Flow
> python -m unittest -q unittest_fixtures.TestFixtures
in module unittest_fixtures - setUpModule()
in class TestFixtures - setUpClass()
in test_1 - setUp()
in test_1 - test_1()
in test_1 - tearDown()
in test_2 - setUp()
in test_2 - test_2()
in test_2 - tearDown()
in class TestFixtures - tearDownClass()
in module unittest_fixtures - tearDownModule()
----------------------------------------------------------------------
Ran 2 tests in 0.026s
OK
Adding Cleanup Calls
Extra cleanup methods can be added from either a test or a setUp method.
Cleanup functions are called AFTER tearDown() but BEFORE tearDownClass()
class TestAddCleanup(TestFixtures):
def setUp(self):
TestFixtures.setUp(self)
# --- add a cleanup method fixture for all tests
def cleanup_a():
self.logPoint()
self.addCleanup(cleanup_a)
def test_1(self):
TestFixtures.test_1(self)
# --- add a cleanup method fixture for just this test
def cleanup_b():
self.logPoint()
self.addCleanup(cleanup_b)
def test_2(self):
TestFixtures.test_1(self)
Output
> python -m unittest -q unittest_fixtures.TestAddCleanup
in module unittest_fixtures - setUpModule()
in class TestAddCleanup - setUpClass()
in test_1 - setUp()
in test_1 - test_1()
in test_1 - tearDown()
in test_1 - cleanup_b()
in test_1 - cleanup_a()
in test_2 - setUp()
in test_2 - test_1()
in test_2 - tearDown()
in test_2 - cleanup_a()
in class TestAddCleanup - tearDownClass()
in module unittest_fixtures - tearDownModule()
----------------------------------------------------------------------
Ran 2 tests in 0.030s
OK
Skipping tests within setUp()
In the setUp method, you can decide to skip a test.
If skipped, the test will not be run.
ALSO, the tearDown method will not be run.
class TestSkip(TestFixtures):
def setUp(self):
TestFixtures.setUp(self)
currentTest = self.id().split('.')[-1]
if currentTest == 'test_2':
self.skipTest('reason for skipping')
# the 'reason' will displayed if '-v/--verbose' flag used
Output
> python -m unittest -q unittest_fixtures.TestSkip
in module unittest_fixtures - setUpModule()
in class TestSkip - setUpClass()
in test_1 - setUp()
in test_1 - test_1()
in test_1 - tearDown()
in test_2 - setUp()
in class TestSkip - tearDownClass()
in module unittest_fixtures - tearDownModule()
----------------------------------------------------------------------
Ran 2 tests in 0.018s
OK (skipped=1)
Exceptions in Fixtures
In all of the examples above, the test fixtures are really simple, and nothing can go wrong.
However, that’s not always the case, of course.
It’s important to understand what happens to your control flow if a test fixture fails in some way.
What happens if a test fixture function hits an assert or an exception?
That depends on the fixture method/function.
But…. that’s a topic for another post.
Specifically, What happens when unittest fixtures fail
Feedback, Please
Hopefully this has been informative, and I hope it will be a valuable reference for both the syntax and flow of unittest fixture functions and methods.
If this is helpful, I’d really like to know. Please leave a comment.
Feedback keeps me going.
Cheers.