# ==============================================================================
# JUNIOR PYTHON TRACK 25 — TESTING & QUALITY ASSURANCE
# ==============================================================================
import pytest
from unittest.mock import Mock, patch
# ------------------------------------------------------------------------------
# 1. Functions & Classes under test
# ------------------------------------------------------------------------------
def add(a: int, b: int) -> int:
return a + b
def divide(a: float, b: float) -> float:
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
class BankAccount:
def __init__(self, balance: float = 0.0):
self.balance = balance
def deposit(self, amount: float) -> None:
if amount <= 0:
raise ValueError("Deposit must be positive")
self.balance += amount
def withdraw(self, amount: float) -> None:
if amount > self.balance:
raise ValueError("Insufficient funds")
self.balance -= amount
def fetch_user_role(user_id: int, api_client: object) -> str:
# Simulates calling an external payment / user service
response = api_client.get_user(user_id)
return response.get("role", "guest")
# ------------------------------------------------------------------------------
# 2. Pytest Unit Tests, Fixtures, Parametrization, Exception & Mock Testing
# ------------------------------------------------------------------------------
# A. Simple Assertion Test
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
# B. Testing Exceptions with pytest.raises
def test_divide_by_zero():
with pytest.raises(ValueError) as exc_info:
divide(10.0, 0.0)
assert "Cannot divide by zero" in str(exc_info.value)
# C. Reusable State via @pytest.fixture
@pytest.fixture
def account():
"""Fixture providing a fresh BankAccount with $100 initial balance."""
return BankAccount(balance=100.0)
def test_account_deposit(account):
account.deposit(50.0)
assert account.balance == 150.0
def test_account_withdraw_insufficient(account):
with pytest.raises(ValueError, match="Insufficient funds"):
account.withdraw(500.0)
# D. Parametrized Matrix Testing
@pytest.mark.parametrize("a, b, expected", [
(1, 1, 2),
(10, 20, 30),
(-5, 5, 0),
(100, 250, 350),
])
def test_add_parametrized(a, b, expected):
assert add(a, b) == expected
# E. Mocking External Dependencies
def test_fetch_user_role_with_mock():
mock_api = Mock()
mock_api.get_user.return_value = {"id": 42, "role": "admin"}
role = fetch_user_role(42, mock_api)
assert role == "admin"
mock_api.get_user.assert_called_once_with(42)
$ pytest test_app.py --cov=app -v
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0
rootdir: /home/user/project
collected 8 items
test_app.py::test_add PASSED [ 12%]
test_app.py::test_divide_by_zero PASSED [ 25%]
test_app.py::test_account_deposit PASSED [ 37%]
test_app.py::test_account_withdraw_insufficient PASSED [ 50%]
test_app.py::test_add_parametrized[1-1-2] PASSED [ 62%]
test_app.py::test_add_parametrized[10-20-30] PASSED [ 75%]
test_app.py::test_add_parametrized[-5-5-0] PASSED [ 87%]
test_app.py::test_fetch_user_role_with_mock PASSED [100%]
---------- coverage: platform linux, python 3.12.3-final-0 -----------
Name Stmts Exec Cover
--------------------------------
app.py 22 22 100%
--------------------------------
TOTAL 22 22 100%
============================== 8 passed in 0.12s ===============================
import unittest
from unittest.mock import Mock
class TestJuniorTestingSuite(unittest.TestCase):
def test_unit_add_assertion(self):
self.assertEqual(add(2, 3), 5)
def test_exception_handling(self):
with self.assertRaises(ValueError):
divide(10.0, 0.0)
def test_bank_account_state(self):
acc = BankAccount(100.0)
acc.deposit(50.0)
self.assertEqual(acc.balance, 150.0)
def test_mocking_external_call(self):
mock_client = Mock()
mock_client.get_user.return_value = {"role": "editor"}
res = fetch_user_role(1, mock_client)
self.assertEqual(res, "editor")
if __name__ == "__main__":
unittest.main()