Testing Like a Senior Engineer
Treat test suites as executable architecture specifications. Test public behavior rather than private implementation details using advanced pytest fixture lifecycles, fast transaction rollback isolation, Fakes over fragile Mocks, and Hypothesis property-based invariants.
Senior Systems Studio: Testing Like a Senior Engineer
Select any senior topic below to verify advanced pytest fixture lifecycles, fast transaction rollback isolation, Fakes over fragile Mocks (InMemoryUserRepository), and Property-Based invariants using Hypothesis.
Executable Architecture Specifications & Test Isolation
- Testing Pyramid: Unit tests run deterministically in-memory; Integration tests verify DB boundaries via rollbacks; E2E tests validate user flows.
- Fakes Over Mocks: Fragile mocks assert internal call counters; Fakes (e.g. `InMemoryUserRepository`) preserve full behavioral contracts across refactors.
- Property-Based Testing: `Hypothesis` auto-generates edge-case inputs to assert system invariants across thousands of randomized test runs.
/ \
/ E2E \ Verify critical user flows across deployments.
/โโโโโโโ\
/ Integr- \ Verify external boundaries (Real DB, Redis, APIs)
/ ation \ via transaction rollbacks or testcontainers.
/โโโโโโโโโโโโโ\
/ Unit Tests \ Fast, deterministic, in-memory domain tests.
/โโโโโโโโโโโโโโโโโ\ Zero disk I/O, zero network, zero real timers.
1. Advanced pytest Architecture & Rollback Isolation
Scope fixtures strategically (`session` for engines, `function` for transactions). Isolate database tests using fast transaction rollbacks rather than destructive table truncation:
import pytest
@pytest.fixture
def db_transaction(db_session):
"""Function-scoped transaction rollback fixture."""
connection = db_session.engine.connect()
transaction = connection.begin()
yield connection
# Instant rollback avoids expensive table truncation or recreating schemas
transaction.rollback()
connection.close()
@pytest.mark.parametrize("role,expected_access", [
("admin", True),
("developer", True),
("guest", False),
])
def test_role_access_matrix(role: str, expected_access: bool):
assert check_access(role) == expected_access
2. Test Doubles: Fakes Over Mocks
Mocks assert internal function call counters and parameter lists, creating brittle tests that break during refactoring. Fakes implement fully functional in-memory behavior:
class InMemoryUserRepository:
"""Fake repository preserving interface contract in RAM."""
def __init__(self):
self._store: dict[str, dict] = {}
def save(self, user_id: str, data: dict) -> None:
self._store[user_id] = data
def get_by_id(self, user_id: str) -> dict | None:
return self._store.get(user_id)
def test_user_onboarding():
repo = InMemoryUserRepository()
service = UserService(repo)
user = service.register_user("u1", "alex@cloud.dev")
assert repo.get_by_id("u1")["email"] == "alex@cloud.dev"
3. Property-Based Testing (`Hypothesis`)
Unit tests verify hand-picked inputs. Property-based tests use `Hypothesis` to generate thousands of input variations, searching for edge cases that break system invariants:
from hypothesis import given, strategies as st
def quicksort(arr: list[int]) -> list[int]:
if len(arr) <= 1: return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quicksort(left) + middle + quicksort(right)
@given(st.lists(st.integers()))
def test_sorting_preserves_length_and_order(xs: list[int]):
sorted_xs = quicksort(xs)
assert len(sorted_xs) == len(xs)
assert all(sorted_xs[i] <= sorted_xs[i + 1] for i in range(len(sorted_xs) - 1))