Skip to content

Testing & Reliability

Automated testing is the “Safety Net” of software engineering. It allows you to change, refactor, and improve your code with the absolute confidence that you haven’t broken existing features.

In Python, we have two main tools: the built-in unittest and the modern industry favorite, pytest.


Inspired by Java’s JUnit, unittest is class-based and included in the Standard Library.

test_logic.py
import unittest
def add(a, b): return a + b
class TestMath(unittest.TestCase):
def test_positive(self):
self.assertEqual(add(1, 2), 3)
if __name__ == "__main__":
unittest.main()

pytest is the standard for modern Python projects. It is simpler to write and much more powerful.

def test_add():
assert add(1, 2) == 3 # No classes required!

Fixtures allow you to set up “state” before a test runs (e.g., creating a temporary database or loading a config file).

test_fixtures.py
import pytest
@pytest.fixture
def sample_user():
return {"id": 1, "name": "Alice"}
def test_user_id(sample_user):
assert sample_user["id"] == 1

What if your function sends an email or talks to a paid API? You don’t want to send real emails during a test. Mocking allows you to replace a real function with a “fake” one.

mock_demo.py
from unittest.mock import MagicMock
# Create a fake database object
db = MagicMock()
db.get_user.return_value = {"name": "Test"}
# Now we can test our logic without a real database!
assert db.get_user(1)["name"] == "Test"

TDD is a workflow where you write the Test before you write the Code.

  1. Red: Write a test for a feature that doesn’t exist yet. Run it and watch it fail.
  2. Green: Write just enough code to make the test pass.
  3. Refactor: Clean up the code, knowing the test will tell you if you break anything.

Featureunittestpytest
ComplexityHigh (Boilerplate).Low (Pythonic).
Setup/TeardownsetUp() method.Fixtures.
DiscoveryManual/Standard.Automatic.
UsageEnterprise/Legacy.Industry Standard.