Pytest for testing Python you actually care about. Tests are not homework – they are the safety net when you refactor idiomatic code into packaged modules and Flask routes.
Install and first test
pip install pytest
# tests/test_helpers.py
from mytool.helpers import normalise_email
def test_normalise_email_strips_and_lowercases():
assert normalise_email(" Alice@Example.com ") == "alice@example.com"
pytest -q
Arrange, act, assert
Structure tests in three beats: set up data, run the behaviour, check the outcome. No mystery about what failed.
def test_add_vat():
# arrange
net = 100.0
# act
gross = add_vat(net, rate=0.20)
# assert
assert gross == 120.0
pytest.raises for exceptions
import pytest
def test_balance_cannot_go_negative():
account = Account(10)
with pytest.raises(ValueError):
account.balance = -1
Fixtures for shared setup
import pytest
from pathlib import Path
@pytest.fixture
def temp_db(tmp_path: Path):
db_file = tmp_path / "test.db"
connection = sqlite3.connect(db_file)
connection.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT)")
yield connection
connection.close()
tmp_path is a built-in fixture – a temporary directory pytest cleans up for you.
Parametrize repeated cases
@pytest.mark.parametrize(
"raw,expected",
[
("alice@example.com", "alice@example.com"),
("", ""),
(" BOB@EXAMPLE.COM", "bob@example.com"),
],
)
def test_normalise_email_cases(raw, expected):
assert normalise_email(raw) == expected
Testing Flask apps
import pytest
from myapp import create_app
@pytest.fixture
def client():
app = create_app({"TESTING": True})
return app.test_client()
def test_index_returns_200(client):
response = client.get("/")
assert response.status_code == 200
What to test first
- Pure functions with clear inputs and outputs.
- Validation and error paths you are afraid to touch.
- HTTP routes that must not regress after a refactor.
Next: CLI tools with argparse and Click – turning your packaged module into something you can run from the terminal.

