Skip to main content

Testing Exceptions, setUp() & tearDown() Fixtures

In enterprise Python codebases, your tests need to do more than assert simple math operations:

  1. You must verify that your code raises the correct exceptions when given bad data.
  2. You must create temporary mock data before every test and clean it up immediately after so tests do not interfere with each other.

1. Testing Expected Exceptions with assertRaises()โ€‹

When a function is supposed to raise an error (for example, throwing a ValueError when a bank withdrawal amount is negative), use self.assertRaises() as a context manager:

import unittest

def withdraw_from_account(current_balance, amount):
if amount <= 0:
raise ValueError("Withdrawal amount must be strictly greater than 0!")
if amount > current_balance:
raise PermissionError("Insufficient funds available.")
return current_balance - amount

class TestBankingRules(unittest.TestCase):

def test_negative_withdrawal_raises_value_error(self):
# Asserts that ValueError is successfully thrown
with self.assertRaises(ValueError):
withdraw_from_account(500, -100)

def test_overdraft_raises_permission_error(self):
with self.assertRaises(PermissionError):
withdraw_from_account(500, 1000)

2. Test Fixtures: setUp() & tearDown()โ€‹

If you have 10 test methods that all need a fresh SQLite database connection or a temporary mock shopping cart:

  • setUp(): Runs automatically before every single test method.
  • tearDown(): Runs automatically after every single test method (even if the test fails!).
import unittest

class TestShoppingCart(unittest.TestCase):

def setUp(self):
# Runs BEFORE each test: Create a clean cart with 1 initial product
self.cart = ["Laptop Sleeve"]
print("\n๐Ÿ”ง Setup: Fresh shopping cart created.")

def tearDown(self):
# Runs AFTER each test: Reset cart state
self.cart.clear()
print("๐Ÿงน Teardown: Cart cleaned up.")

def test_add_item(self):
self.cart.append("Wireless Mouse")
self.assertEqual(len(self.cart), 2)

def test_cart_has_initial_item(self):
self.assertIn("Laptop Sleeve", self.cart)
self.assertEqual(len(self.cart), 1)

3. Class-Level Fixtures: setUpClass() & tearDownClass()โ€‹

If setting up your resource is heavy and expensive (like creating an in-memory SQLite database table), you don't want to recreate it 50 times.

Use @classmethod setUpClass(cls) to run setup once for the entire test class:

import unittest
import sqlite3

class TestDatabaseQueries(unittest.TestCase):

@classmethod
def setUpClass(cls):
# Runs ONCE before all tests in this class
cls.conn = sqlite3.connect(":memory:")
cls.cursor = cls.conn.cursor()
cls.cursor.execute("CREATE TABLE products (id INT, name TEXT)")
cls.cursor.execute("INSERT INTO products VALUES (1, 'Mechanical Keyboard')")

@classmethod
def tearDownClass(cls):
# Runs ONCE after all tests complete
cls.conn.close()

def test_fetch_product(self):
self.cursor.execute("SELECT name FROM products WHERE id = 1")
item = self.cursor.fetchone()
self.assertEqual(item[0], "Mechanical Keyboard")

Quick Summaryโ€‹

  • Testing Exceptions: Use with self.assertRaises(ExpectedException): to verify invalid input triggers correct errors.
  • Method Fixtures (setUp/tearDown): Execute before and after each individual test method to ensure clean isolated test state.
  • Class Fixtures (setUpClass/tearDownClass): Run once per test class for expensive shared resources (e.g. database schemas).

What's Next?โ€‹

Let's explore the industry-standard modern testing framework with expressive asserts and powerful parametrization in Introduction to pytest Framework!