Unit Testing Quick Reference¶
Why Unit Tests?¶
- Validation: Automatically check if your code works
- Specification: Tests show exactly what your function should do
- Debugging: Failed tests tell you what's wrong
- Safety net: Catch bugs before submission
- Professional practice: Real developers write tests first
Basic Structure¶
Simple Test File Template¶
import unittest
from your_module import your_function
class TestYourFunction(unittest.TestCase):
def test_basic_case(self):
"""Test the happy path"""
result = your_function(input)
self.assertEqual(result, expected)
def test_edge_case(self):
"""Test boundary conditions"""
result = your_function(edge_input)
self.assertEqual(result, expected)
if __name__ == '__main__':
unittest.main()
Common Assertions¶
| Assertion | Purpose | Example |
|---|---|---|
assertEqual(a, b) |
Check if values are equal | self.assertEqual(result, 5) |
assertNotEqual(a, b) |
Check if values differ | self.assertNotEqual(x, y) |
assertTrue(x) |
Check if condition is True | self.assertTrue(result > 0) |
assertFalse(x) |
Check if condition is False | self.assertFalse(result == None) |
assertIn(a, b) |
Check if a is in b | self.assertIn("cat", ["cat", "dog"]) |
assertIsNone(x) |
Check if value is None | self.assertIsNone(result) |
assertRaises(Exception, func, args) |
Check if exception raised | self.assertRaises(ValueError, func, bad_arg) |
Running Tests¶
Run all tests in a file:¶
The -v flag shows verbose output (which tests passed/failed)
Run a specific test class:¶
Run a specific test method:¶
Writing Good Tests¶
1. Test One Thing¶
Each test should verify one behavior:
# ✅ GOOD - one clear assertion
def test_empty_input(self):
result = match_treasures_to_locations([], [])
self.assertEqual(result, [])
# ❌ BAD - testing multiple things
def test_everything(self):
result1 = match_treasures_to_locations([], [])
result2 = match_treasures_to_locations([("x", (0,0))], [("y", (0,0))])
self.assertEqual(result1, [])
self.assertEqual(result2, [("x", "y")])
2. Clear Test Names¶
Test names should describe what they test:
# ✅ GOOD - describes what it tests
def test_returns_empty_list_when_no_treasures_match()
# ❌ BAD - vague
def test_function()
3. Arrange-Act-Assert Pattern¶
def test_matches_treasures_to_locations(self):
# ARRANGE: Set up test data
treasures = [("Gold", (0, 0))]
locations = [("Place", (0, 0))]
# ACT: Call the function
result = match_treasures_to_locations(treasures, locations)
# ASSERT: Check the result
self.assertEqual(result, [("Gold", "Place")])
4. Test Edge Cases¶
def test_empty_inputs(self): ...
def test_no_matches_found(self): ...
def test_duplicate_coordinates(self): ...
def test_single_item(self): ...
Debugging with Tests¶
When a test fails:¶
-
Read the error message carefully
This tells you your output doesn't match expected -
Add print statements to understand flow
-
Test simpler cases first
- Start with single items
- Build to complex cases
-
Isolate which part fails
-
Check assumptions
- Are you handling coordinates correctly?
- Are you comparing the right values?
- Are you returning the right type?
Test-Driven Development (TDD)¶
The recommended workflow:
Step 1: Write Tests (before coding!)¶
def test_matches_treasures_to_locations(self):
treasures = [("Gold", (40.741895, -73.989308))]
locations = [("New York", (40.741895, -73.989308))]
expected = [("Gold", "New York")]
self.assertEqual(match_treasures_to_locations(treasures, locations), expected)
Step 2: Run Tests (they fail - that's expected!)¶
FAIL: test_matches_treasures_to_locations
NameError: name 'match_treasures_to_locations' is not defined
Step 3: Write Minimal Code¶
Step 4: Run Tests Again¶
Step 5: Implement Solution¶
def match_treasures_to_locations(treasures, locations):
matches = []
for treasure_name, treasure_coords in treasures:
for location_name, location_coords in locations:
if treasure_coords == location_coords:
matches.append((treasure_name, location_name))
return matches
Step 6: Run Tests (they pass!)¶
Step 7: Add More Tests¶
Repeat from Step 1 for edge cases
Common Mistakes¶
❌ Forgetting to import the function¶
❌ Not running tests before submission¶
❌ Testing too much in one test¶
# Separate these into different tests:
def test_everything(self):
self.assertEqual(...stuff1...)
self.assertEqual(...stuff2...)
self.assertEqual(...stuff3...)
❌ Ignoring edge cases¶
Resources¶
Quick Checklist Before Submission¶
- I imported my function correctly
- All my tests run:
python -m unittest test_*.py -v - Tests pass (all green)
- I tested edge cases (empty, single item, no matches, etc.)
- My test file is named
test_*.py - I included both
function.pyANDtest_function.pyin submission - My function has clear comments
- I followed Python naming conventions