Unit testing is the process of checking individual pieces (units) of your code, such as functions, to make sure they behave correctly in isolation. Python doesn't force you to write tests, but using a tool like pytest makes testing simple and powerful.
Unit Testing
What is Unit Testing?
Pytest
Pytest is a Python testing framework that automates running your tests. Install it with:
pip install pytest
Then you can write a test similar to
# In test_file.py
from file_to_test import function_to_test
def test_function_to_test():
assert function_to_test(3) == 6
This test will pass if
function_to_test(3)
returns 6, and fail otherwise, raising an
AssertionError.
You can run your test from the terminal with:
pytest test_file.py
Pytest will search for all functions in that file starting with test_ and run them.
The assert statement checks if a condition is True. If it's not, it raises an AssertionError.
assert 2 + 2 == 4 # Passes
assert 2 + 2 == 5 # Raises AssertionError
Sometimes, you want to handle failed assertions manually with try and except keywords. This method can be useful when debugging or logging failures, though it's more common to let pytest handle assertions automatically.
def test_func():
try:
assert function_to_test(3) == 6
except AssertionError:
print("Test failed: output did not match expected value")
Errors
Errors in Python are called exceptions. They're all subclasses of the built-in class
BaseException.
There are two main kinds of errors; syntax errors and runtime errors.
- Syntax Errors: Raised before the program runs and arise from incorrectly written code.
- Runtime Errors: Raised while the program is running.
Runtime Errors
| Exception | Description |
|---|---|
| SyntaxError | Code is not syntactically correct |
| NameError | Variable not defined |
| TypeError | Invalid type operation (e.g., modifying a tuple) |
| IndexError | List index out of range |
| KeyError | Dictionary key doesn't exist |
| FileNotFoundError | File does not exist |
| ZeroDivisionError | Dividing by zero |
| AssertionError | Assertion failed |
| ValueError | Wrong number/type of values |
Raising Your Own Errors
You can raise errors yourself with custom messages.
raise ValueError("This is a custom error message.")
Handling Exceptions
Use try/except blocks to catch and handle exceptions without crashing your program.
try:
risky_code()
except ZeroDivisionError:
print("You can't divide by zero!")
Full Structure: try, except, else, finally.
try:
result = 10 / 2
except ZeroDivisionError:
print("Cannot divide by zero.")
else:
print("Division successful:", result)
finally:
print("This always runs.")
- try: Run this block
- except: Handle an error if one happens
- else: Runs if no exception occurred
- finally: Always runs, useful for cleanup
You can use exception handling inside a loop for repeated attempts:
while True:
try:
value = int(input("Enter a number: "))
except ValueError:
print("That wasn't a number. Try again.")
else:
break
Ignoring Errors
try:
risky_thing()
except FileNotFoundError:
pass # Silently ignore the error
Error Information
You can get extra information from your error using the errorName as e syntax. Here 'e' is used similar to the this keyword where it will refer to the error.
try:
open("file.txt")
except FileNotFoundError as e:
print(f"Error: {e.filename} not found.")