Exceptions and Testing
Use ← → arrow keys or Space to navigate | Press F for fullscreen
Handle failures gracefully and verify that code does what it claims
The program stops because Python raises an exception.
age = int("twenty")
# ValueError
Exception handling lets the program recover or report a useful message.
The program runs, but the answer is wrong.
def average(nums):
return sum(nums) / (len(nums) - 1)
# wrong formula
Tests catch wrong behavior before users do.
Tracebacks, try/except, else/finally, custom exceptions, and logging
def parse_age(text):
return int(text)
def register(raw_age):
age = parse_age(raw_age)
return {"age": age}
register("twenty")
# ValueError: invalid literal for int()
try / excepttry block.except:. It can hide real bugs.
def parse_int(text):
try:
return int(text)
except ValueError:
print(f"{text!r} is not an integer")
return None
print(parse_int("42")) # 42
print(parse_int("hello")) # None
Different failures deserve different responses.
| Exception | Common cause |
|---|---|
ValueError | Invalid value, like int("x") |
FileNotFoundError | Missing file path |
KeyError | Missing dictionary key |
ZeroDivisionError | Division by zero |
def get_score(scores, name):
try:
return scores[name]
except KeyError:
return "missing student"
except TypeError:
return "scores must be a dict"
else and finallyelse runs only if no exception occurs.finally always runs, whether an exception happened or not.finally for cleanup.try:
value = int("42")
except ValueError:
print("bad input")
else:
print("converted:", value)
finally:
print("done")
raise when a function cannot honor its contract.class OverdraftError(Exception):
pass
def withdraw(balance, amount):
if amount < 0:
raise ValueError("amount must be positive")
if amount > balance:
raise OverdraftError("insufficient funds")
return balance - amount
Logging records diagnostic information without relying on scattered print() calls.
| Level | Use |
|---|---|
DEBUG | Detailed developer info |
INFO | Normal progress |
WARNING | Unexpected but recoverable |
ERROR | Operation failed |
import logging
logging.basicConfig(level=logging.INFO)
def divide(a, b):
try:
return a / b
except ZeroDivisionError:
logging.error("division by zero")
return None
pytest, unittest, doctest, fixtures, mocking, parametrization, and coverage
def add(a, b):
return a + b
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
pytesttest_.test_.assert statements.python -m pytest.
# test_calc.py
from calc import add, divide
def test_add():
assert add(2, 3) == 5
def test_divide():
assert divide(10, 2) == 5
Good tests check both normal behavior and expected failure behavior.
pytest.raises for expected exceptions.import pytest
def divide(a, b):
if b == 0:
raise ZeroDivisionError("b cannot be 0")
return a / b
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
unittestassertEqual.unittest in many older or standard-library-oriented projects.
import unittest
class TestCalc(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 3), 5)
if __name__ == "__main__":
unittest.main()
def square(x):
"""Return x squared.
>>> square(4)
16
>>> square(-3)
9
"""
return x * x
| Tool | Use |
|---|---|
| Parametrize | Run one test with many inputs |
| Fixture | Reusable setup data or objects |
| Mock | Replace slow or external dependencies |
| Coverage | Find code paths tests did not run |
import pytest
@pytest.mark.parametrize(
"a,b,expected",
[(2, 3, 5), (0, 0, 0), (-1, 1, 0)]
)
def test_add_cases(a, b, expected):
assert add(a, b) == expected
| Concept | Key syntax / notes |
|---|---|
| Catch exception | try: / except ValueError: |
| Exception object | except Exception as e: |
| Cleanup | finally always runs |
| Raise exception | raise ValueError("message") |
| Logging | logging.info(), logging.error() |
| pytest test | def test_name(): assert result == expected |
| Expected exception | with pytest.raises(Error): |
| unittest | class TestX(unittest.TestCase): |
| doctest | Examples in docstrings with >>> |
Next up: OOP
tracebacks · exceptions · logging · pytest · unittest · doctest