Testing with pytest & unittest.mock
Write professional tests with pytest fixtures, parametrize, monkeypatching, unittest.mock, pytest-asyncio, and coverage reports.
Part 1: What You Will Learn
- Write focused tests with pytest.
- Share setup using fixtures.
- Run several input cases with
@pytest.mark.parametrize. - Replace external dependencies with
unittest.mock.
Part 2: Key Concepts
A good unit test checks one small behaviour and remains fast, deterministic, and independent from external systems. pytest reduces test boilerplate, while unittest.mock lets you replace collaborators such as email senders, API clients, clocks, or repositories.
Part 3: Topic-Specific Code Examples
def calculate_discount(total: float, member: bool) -> float:
if total < 0:
raise ValueError("total cannot be negative")
rate = 0.10 if member else 0.0
return round(total * (1 - rate), 2)
def checkout(total: float, member: bool, notifier) -> float:
final_total = calculate_discount(total, member)
notifier.send(f"Payment due: RM{final_total:.2f}")
return final_totalimport pytest
from unittest.mock import Mock
from app import calculate_discount, checkout
@pytest.fixture
def notifier() -> Mock:
return Mock()
@pytest.mark.parametrize(
("total", "member", "expected"),
[
(100.0, False, 100.0),
(100.0, True, 90.0),
(250.0, True, 225.0),
],
)
def test_calculate_discount(total, member, expected):
assert calculate_discount(total, member) == expected
def test_negative_total_is_rejected():
with pytest.raises(ValueError):
calculate_discount(-1, True)
def test_checkout_sends_notification(notifier):
result = checkout(100.0, True, notifier)
assert result == 90.0
notifier.send.assert_called_once_with("Payment due: RM90.00")python -m pip install pytest pytest-cov pytest -q pytest --cov=app --cov-report=term-missing
Part 4: How the Example Works
The parameterised test reuses one test function for three input combinations. pytest.raises() verifies the error path. The mock notifier records how it was used, so the test can confirm that checkout() sends exactly one expected message without contacting a real service.
Part 5: Hands-On Practice
Mini project โ Test a Grade Service. Build a grade(mark) function and test boundary values such as 0, 49, 50, 79, 80, and 100. Then mock a repository in a save_result() function and verify that the expected data is saved.
Part 6: Next Steps
Run and modify the examples in Visual Studio 2026, then continue to Lesson 18. Return to Python Tutorial Home to review the complete curriculum.