Testing Python Code with pytest: Fixtures and Parametrize
pytest is the standard testing tool for Python. Tests are plain functions using plain assert, and pytest's output shows exactly what differed.
First tests
# test_pricing.py
import pytest
from pricing import apply_discount
def test_ten_percent_discount():
assert apply_discount(1000, percent=10) == 900
def test_discount_cannot_be_negative():
with pytest.raises(ValueError, match="percent"):
apply_discount(1000, percent=-5)
uv run pytest -q
uv run pytest -k discount -x # only matching tests, stop at first failure
Parametrize: many cases, one test
@pytest.mark.parametrize(
("price", "percent", "expected"),
[
(1000, 0, 1000),
(1000, 25, 750),
(999, 10, 899), # rounding down
(0, 50, 0),
],
)
def test_apply_discount(price, percent, expected):
assert apply_discount(price, percent=percent) == expected
Fixtures: reusable setup
# conftest.py - fixtures here are available to every test file
import pytest
@pytest.fixture
def db():
conn = create_test_database()
yield conn # the test runs here
conn.close() # teardown
@pytest.fixture
def user(db):
return db.users.create(email="test@example.com")
# test_orders.py
def test_new_user_has_no_orders(db, user):
assert db.orders.for_user(user.id) == []
Built-in fixtures
def test_writes_report(tmp_path):
out = tmp_path / "report.csv"
write_report(out)
assert out.read_text().startswith("id,total")
def test_uses_api_key(monkeypatch):
monkeypatch.setenv("API_KEY", "test-key")
assert load_config().api_key == "test-key"
Coverage
uv add --dev pytest-cov
uv run pytest --cov=src --cov-report=term-missing
Aim coverage at the code that matters (business rules, edge cases), rather than chasing 100%.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.