Error Handling, Debugging, and Basic Testing Practices
Error handling, debugging, and testing cover what happens when code does not do what you intended. The exam tests the difference between error categories, correct use of exceptions, and how to write a test that would actually catch a regression.
Error categories
Syntax errors stop the program from running at all. Runtime exceptions occur during execution: ZeroDivisionError, IndexError, KeyError, TypeError, FileNotFoundError. Logic errors run but produce the wrong answer. Recognising the category narrows the search immediately.
Exceptions
try/except/finally (or try/catch/finally) lets you handle anticipated failures. Catch the specific exception you expect, and let the rest propagate. finally runs whether or not an exception occurred, including when the try block returns. raise re-raises or throws deliberately; custom exception classes express domain failures. Reading a traceback from the bottom up gives the exception type and message, then the call chain.
Debugging and testing
Debugging is systematic: reproduce, isolate, inspect state (print, logging, breakpoint), form a hypothesis, change one thing, retest. Unit tests call a function with known inputs and assert on the output. Good tests cover normal cases, boundaries (0, empty, maximum), and invalid input, and they fail for the right reason.
Worked example
def safe_div(a, b):
try:
return a / b
except ZeroDivisionError:
return None
finally:
print('done')
Calling safe_div(1, 0) returns None and still prints 'done', because finally executes before the function actually returns. Replacing the specific clause with a bare except: would also hide a TypeError from bad input, masking a real bug.
Traps
- A bare
except:swallowing every error, including typos. - Ordering handlers so a parent exception catches before the specific one.
- Using
assertfor user-facing validation, since assertions can be disabled. - Testing only the happy path.
- Fixing the symptom without reproducing the cause.