Python Test Fixtures
Summary: in this tutorial, you’ll learn about Python test fixtures including setUp()
and tearDown()
methods.
Introduction to the Python Test fixtures
By definition, a test fixture is a function or method that runs before and after a block of test code executes. In other words, it is a step carried out before or after a test.
Module-level fixtures
Suppose you have a test module called test_my_module.py
. In the test_my_module.py
, the setUpModule()
and tearDownModule()
functions are the module-level fixtures.
- The
setUpModule()
function runs before all test methods in the test module. - The
tearDownModule()
function runs after all methods in the test module.
See the following example:
import unittestdef setUpModule():
print('Running setUpModule')
def tearDownModule():
print('Running tearDownModule')
class TestMyModule(unittest.TestCase):
def test_case_1(self):
self.assertEqual(5+5, 10)
def test_case_2(self):
self.assertEqual(1+1, 2)
Code language: Python (python)
If you run the test:
python -m unittest -v
Code language: Python (python)
Output:
Running setUpModule
test_case_1 (test_my_module.TestMyModule) ... ok
test_case_2 (test_my_module.TestMyModule) ... ok
Running tearDownModule----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
Code language: Python (python)
In this example, the setUpModule()
function runs before all the test methods and the tearDownModule()
function runs after all the test methods.
Class-level fixtures
The setUpClass()
and tearDownClass()
are class-level fixtures:
- The
setUpClass()
runs before all test methods of a class - The
tearDownClass()
runs after all test methods of a class.
For example:
import unittestdef setUpModule():
print('Running setUpModule')
def tearDownModule():
print('Running tearDownModule')
class TestMyModule(unittest.TestCase):
def setUpClass(cls):
print('Running setUpClass')
def tearDownClass(cls):
print('Running tearDownClass')
def test_case_1(self):
self.assertEqual(5+5, 10)
def test_case_2(self):
self.assertEqual(1+1, 2)
Code language: Python (python)
In this example, we added the class methods: setUpClass()
and tearDownClass()
to the TestMyModule
class.
If you run the test, you’ll see the following output:
Running setUpModule
Running setUpClass
test_case_1 (test_my_module.TestMyModule) ... ok
test_case_2 (test_my_module.TestMyModule) ... ok
Running tearDownClass
Running tearDownModule----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
Code language: Python (python)
Method-level fixtures
The setUp()
and tearDown()
are method-level fixtures:
- The
setUp()
runs before every test method in the test class. - The
tearDown()
runs after every test method in the test class.
For example:
import unittestdef setUpModule():
print('Running setUpModule')
def tearDownModule():
print('Running tearDownModule')
class TestMyModule(unittest.TestCase):
def setUpClass(cls):
print('Running setUpClass')
def tearDownClass(cls):
print('Running tearDownClass')
def setUp(self):
print('')
print('Running setUp')
def tearDown(self):
print('Running tearDown')
def test_case_1(self):
print('Running test_case_1')
self.assertEqual(5+5, 10)
def test_case_2(self):
print('Running test_case_2')
self.assertEqual(1+1, 2)
Code language: Python (python)
The following shows the test result:
Running setUpModule
Running setUpClass
test_case_1 (test_my_module.TestMyModule) ...
Running setUp
Running test_case_1
Running tearDown
ok
test_case_2 (test_my_module.TestMyModule) ...
Running setUp
Running test_case_2
Running tearDown
ok
Running tearDownClass
Running tearDownModule----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
Code language: Python (python)
In this example, the setUp()
and tearDown()
executes before and after each test method including test_case_1()
and test_case_2()
.
Python test fixtures example
First, define classes called BankAccount
and InsufficientFund
classes in the bank_account.py
module:
class InsufficientFund(Exception):
passclass BankAccount:
def __init__(self, balance: float) -> None:
if balance < 0:
raise ValueError('balance cannot be negative')
self._balance = balance
def balance(self) -> float:
return self._balance
def deposit(self, amount: float) -> None:
if amount <= 0:
raise ValueError('The amount must be positive')
self._balance += amount
def withdraw(self, amount: float) -> None:
if amount <= 0:
raise ValueError('The withdrawal amount must be more than 0')
if amount > self._balance:
raise InsufficientFund('Insufficient ammount for withdrawal')
self._balance -= amount
Code language: Python (python)
Second, define the TestBankAccount
class in the test_bank_account.py
module:
import unittestfrom bank_account import BankAccount
class TestBankAccount(unittest.TestCase):
def test_deposit(self):
self.bank_account = BankAccount(100)
self.bank_account.deposit(100)
self.assertEqual(self.bank_account.balance, 200)
def test_withdraw(self):
self.bank_account = BankAccount(100)
self.bank_account.withdraw(50)
self.assertEqual(self.bank_account.balance, 50)
Code language: Python (python)
The TestBankAccount
class has two test methods:
test_
– test thedeposit()
deposit()
method of the bank account.test_
– test thewithdraw()
withdraw()
method of the bank account.
Both methods create a new instance of the BankAccount
. It’s redundant.
To avoid redundancy, you can create an instance of the BankAccount class in setUp()
method and use it in all the test methods:
import unittestfrom bank_account import BankAccount
class TestBankAccount(unittest.TestCase):
def setUp(self) -> None:
self.bank_account = BankAccount(100)
def test_deposit(self):
self.bank_account.deposit(100)
self.assertEqual(self.bank_account.balance, 200)
def test_withdraw(self):
self.bank_account.withdraw(50)
self.assertEqual(self.bank_account.balance, 50)
Code language: Python (python)
In the
method:setUp()
- First, create an instance of the
BankAccount
class and assign it to the instance variableself.bank_account
. - Then, use
self.bank_account
instance in bothtest_deposit()
andtest_withdraw()
methods.
When running test methods test_deposit()
and test_withdraw()
, the setUp()
runs before each test method.
For test_deposit()
method:
setUp()
test_deposit()
Code language: Python (python)
For test_withdraw()
method:
setUp()
test_withdraw()
Code language: Python (python)
If you run the test:
python -m unittest -v
Code language: Python (python)
It’ll output the following:
test_deposit (test_bank_account.TestBankAccount) ... ok
test_withdraw (test_bank_account.TestBankAccount) ... ok----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
Code language: Python (python)
The following adds the tearDown()
method to the TestBankAccount
:
import unittestfrom bank_account import BankAccount, InsufficientFund
class TestBankAccount(unittest.TestCase):
def setUp(self) -> None:
self.bank_account = BankAccount(100)
def test_deposit(self):
self.bank_account.deposit(100)
self.assertEqual(self.bank_account.balance, 200)
def test_withdraw(self):
self.bank_account.withdraw(50)
self.assertEqual(self.bank_account.balance, 50)
def tearDown(self) -> None:
self.bank_account = None
Code language: Python (python)
The tearDown()
method assigns None
to the self.bank_account
instance.
Summary
- Fixtures are functions and methods that execute before and after test code blocks execute.
- The
setUpModule()
andtearDownModule()
run before and after all test methods in the module. - The
setUpclass()
andtearDownClass()
run before and after all test methods in a test class. - The
setUp()
andtearDown()
run before and after each test method of a test class.