python:testing
Testing with pytest
Stub-Driven TDD and layer boundary testing patterns for Python applications.
Core Principle: Stub-Driven TDD
Test at component boundaries, not internal implementation:
Router → Service → Repository → Entity → Database
↓ ↓ ↓ ↓
Test Test Test Test
Follow the Stub → Test → Implement → Refactor workflow:
- Stub - Create function signature with
pass - Test - Write test for expected behavior
- Implement - Make test pass
- Refactor - Clean up code
# 1. Stub
def calculate_discount(total: Decimal) -> Decimal:
pass
# 2. Test
def test_discount_for_large_order():
result = calculate_discount(Decimal("150"))
assert result == Decimal("15")
# 3. Implement
def calculate_discount(total: Decimal) -> Decimal:
if total > 100:
return total * Decimal("0.1")
return Decimal("0")
Layer Boundary Testing Overview
Test what crosses layer boundaries, not internal implementation:
- Entity Layer: Domain logic, validation, transformations (from_request, to_response, to_record)
- Service Layer: Business workflows, error handling, dependency orchestration
- Repository Layer: CRUD operations, query logic, entity ↔ record transformations
- Router Layer: Request validation, response serialization, status codes
See references/boundaries.md for comprehensive layer-specific examples.
Entity Testing Example
Test transformations and business logic:
def test_product_from_request():
"""Test creation from request"""
request = CreateProductRequest(name="Widget", price=Decimal("9.99"))
product = Product.from_request(request)
assert product.name == "Widget"
assert product.price == Decimal("9.99")
assert isinstance(product.id, UUID)
def test_product_apply_discount():
"""Test business logic"""
product = Product(id=uuid4(), name="Widget", price=Decimal("100"))
discounted = product.apply_discount(Decimal("0.1"))
assert discounted.price == Decimal("90")
Service Testing Example
Test orchestration with stubbed dependencies:
from unittest.mock import Mock
def test_create_product_service():
"""Test with mocked repository"""
mock_repo = Mock()
mock_repo.save.return_value = Product(id=uuid4(), name="Widget")
service = ProductService(repo=mock_repo)
result = service.create(CreateProductRequest(name="Widget", price=Decimal("9.99")))
mock_repo.save.assert_called_once()
assert result.name == "Widget"
Repository Testing Example
Test data access with real test database:
@pytest.fixture
def test_db():
"""In-memory test database"""
engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
return sessionmaker(bind=engine)()
def test_repository_save(test_db):
"""Test database operations"""
repo = ProductRepository(test_db)
product = Product(id=uuid4(), name="Widget", price=Decimal("9.99"))
saved = repo.save(product)
assert saved.id == product.id
assert test_db.query(ProductRecord).count() == 1
Router Testing Example
Test HTTP layer with TestClient:
from fastapi.testclient import TestClient
def test_create_product_endpoint():
"""Test POST endpoint"""
client = TestClient(app)
response = client.post(
"/products",
json={"name": "Widget", "price": 9.99},
)
assert response.status_code == 201
assert response.json()["name"] == "Widget"
Test Organization Basics
tests/
├── unit/
│ ├── test_entities.py # Entity + Value object tests
│ └── test_services.py # Service tests (with mocks)
├── integration/
│ ├── test_repositories.py # Repository tests (with DB)
│ └── test_endpoints.py # Router tests (with client)
└── conftest.py # Shared fixtures
Reference Documentation
For comprehensive patterns and examples, see:
- references/boundaries.md - Layer boundary testing patterns with complete examples for each layer
- references/mocking.md - Mock strategies, verification methods, and anti-patterns
- references/pytest.md - Configuration, fixtures, markers, parametrization, and debugging
Progressive disclosure: SKILL.md provides quick reference, references/ contain full details.
More from martinffx/claude-code-atelier
python:architecture
Python application architecture with functional core, effectful shell, DDD, and data modeling. Use when designing application layers, separating pure business logic from IO, defining domain models, implementing validation, or structuring bounded contexts.
14python:monorepo
Python monorepo architecture with uv workspaces, mise, and apps/packages pattern. Use when setting up project structure, configuring workspaces, managing dependencies across packages, or designing multi-app Python repositories.
13python:build-tools
Python project tooling with uv, mise, ruff, basedpyright, and pytest. Use when setting up pyproject.toml, running builds, typechecking, configuring tests, linting, formatting, or managing Python environments.
12python:sqlalchemy
SQLAlchemy ORM patterns for Python database access. Use when defining models, writing queries, implementing upserts, working with JSON columns, or managing database sessions.
12python:fastapi
Building REST APIs with FastAPI, Pydantic validation, and OpenAPI. Use when creating routes, handling requests, designing endpoints, implementing validation, error responses, pagination, or generating API documentation.
11python:temporal
Temporal workflow orchestration in Python. Use when designing workflows, implementing activities, handling retries, managing workflow state, or building durable distributed systems.
9