Unit Testing
Unit testing is essential for writing reliable, maintainable code.
Python's built-in unittest module provides a comprehensive testing
framework inspired by JUnit. Learn to write test cases, use assertions, test
exceptions, and organize your tests effectively. Good tests catch bugs early and
give confidence when refactoring!
Basic Test Structure
The unittest module provides the TestCase class that you
inherit from. Test methods must start with test_ to be automatically
discovered. Use assertion methods like assertEqual,
assertTrue, and assertRaises to verify expected
behavior.
Click Run to execute your code
- Methods starting with
test_ are automatically discovered and run- Use descriptive names:
test_add_positive_numbers,
test_divide_by_zero_raises_error- One test method should test one specific behavior
- Keep tests simple and focused
Common Assertions
The TestCase class provides many assertion methods to verify expected
behavior. Use the most specific assertion that fits your test case for clearer
error messages.
Click Run to execute your code
assertEqual
instead of assertTrue(x == y). Specific assertions provide better
error messages showing the expected and actual values when tests fail!
Testing Exceptions
Use assertRaises to verify that functions raise expected exceptions
under certain conditions. This ensures your error handling works correctly.
Click Run to execute your code
setUp and tearDown
The setUp method runs before each test method, and
tearDown runs after. Use them to initialize test fixtures (common
test data) and clean up resources. This keeps test methods focused on testing
logic rather than setup code.
Click Run to execute your code
setUp to create fresh test data for each test!
Common Mistakes
1. Forgetting 'test_' prefix on test methods
# Wrong - won't be discovered by unittest
class TestMath(unittest.TestCase):
def check_add(self): # No 'test_' prefix!
self.assertEqual(add(2, 3), 5)
# Correct - method starts with 'test_'
class TestMath(unittest.TestCase):
def test_add(self): # Correct prefix
self.assertEqual(add(2, 3), 5)
2. Testing multiple things in one test
# Wrong - multiple assertions for different behaviors
def test_math_operations(self):
self.assertEqual(add(2, 3), 5)
self.assertEqual(subtract(5, 2), 3)
self.assertEqual(multiply(4, 3), 12)
# If first fails, you don't know if others work
# Correct - one test per behavior
def test_add(self):
self.assertEqual(add(2, 3), 5)
def test_subtract(self):
self.assertEqual(subtract(5, 2), 3)
def test_multiply(self):
self.assertEqual(multiply(4, 3), 12)
3. Not testing edge cases and error conditions
# Wrong - only testing happy path
def test_divide(self):
self.assertEqual(divide(10, 2), 5)
# Correct - test normal case AND edge cases
def test_divide_normal(self):
self.assertEqual(divide(10, 2), 5)
def test_divide_by_zero(self):
with self.assertRaises(ValueError):
divide(10, 0)
def test_divide_negative(self):
self.assertEqual(divide(-10, 2), -5)
4. Tests that depend on execution order
# Wrong - tests depend on each other
class TestCounter(unittest.TestCase):
counter = 0 # Class variable - shared state!
def test_increment_first(self):
TestCounter.counter += 1
self.assertEqual(TestCounter.counter, 1)
def test_increment_second(self):
TestCounter.counter += 1
self.assertEqual(TestCounter.counter, 2) # Fails if run alone!
# Correct - each test is independent
class TestCounter(unittest.TestCase):
def setUp(self):
self.counter = 0 # Fresh instance variable per test
def test_increment(self):
self.counter += 1
self.assertEqual(self.counter, 1)
Exercise: Write Comprehensive Tests
Task: Write a complete test suite for a Calculator
class that has methods for basic math operations. Test both normal cases and
edge cases.
Requirements:
- Create a
TestCalculatorclass inheriting fromunittest.TestCase - Use
setUpto create a Calculator instance - Test
addmethod: positive numbers, negative numbers, zero - Test
dividemethod: normal division and division by zero (should raise ValueError) - Test
multiplymethod: normal case, with zero, negative numbers - Run the tests and verify they all pass
Click Run to execute your code
Show Solution
import unittest
class Calculator:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
def multiply(self, a, b):
return a * b
def divide(self, a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
class TestCalculator(unittest.TestCase):
def setUp(self):
"""Set up test fixtures before each test method."""
self.calc = Calculator()
def test_add_positive(self):
self.assertEqual(self.calc.add(2, 3), 5)
def test_add_negative(self):
self.assertEqual(self.calc.add(-2, -3), -5)
def test_add_zero(self):
self.assertEqual(self.calc.add(5, 0), 5)
def test_divide_normal(self):
self.assertEqual(self.calc.divide(10, 2), 5.0)
def test_divide_by_zero(self):
with self.assertRaises(ValueError):
self.calc.divide(10, 0)
def test_multiply_normal(self):
self.assertEqual(self.calc.multiply(3, 4), 12)
def test_multiply_with_zero(self):
self.assertEqual(self.calc.multiply(5, 0), 0)
def test_multiply_negative(self):
self.assertEqual(self.calc.multiply(-2, 3), -6)
if __name__ == '__main__':
unittest.main()
Summary
- unittest Module: Python's built-in testing framework, inspired by JUnit
- TestCase: Inherit from
unittest.TestCaseto create test classes - Test Methods: Methods starting with
test_are automatically discovered and run - Assertions: Use
assertEqual,assertTrue,assertRaises, etc. for verification - setUp/tearDown: Run before/after each test method for fixture setup and cleanup
- Testing Exceptions: Use
assertRaisesto verify functions raise expected exceptions - Test Independence: Tests should be independent and runnable in any order
- Best Practices: One test per behavior, test edge cases, use descriptive names, keep tests simple
What's Next?
Unit testing ensures your code works correctly! Next, we'll explore
logging, which helps you track application behavior and debug
issues in production. The logging module is much more powerful than
print() statements and is essential for professional Python
applications!
Enjoying these tutorials?