Python Testing Skill
Purpose
Pytest-first testing emphasizing fakes over mocks and behavior-driven assertions. This skill applies to all Python testing work—always follow its guidance.
Core Philosophy
Bias toward business logic tests over fakes (Layer 4). Fakes track mutations without coupling to implementation. See references/test-doubles.md for details.
Required Tools
pytest, pytest-cov, pytest-asyncio, pytest-mock, hypothesis
Directory Structure
project/
├── src/mypackage/
└── tests/
├── conftest.py
├── unit/
│ ├── fakes/ # Layer 1: Fake tests
│ └── services/ # Layer 4: Business logic
├── integration/ # Layer 2: Sanity tests
└── e2e/ # Layer 5: Real systems
See references/test-layers.md for layer distribution and decision tree.
Quick Patterns
Prefer Fakes Over Mocks
YOU MUST use fakes for business logic tests. Never use mocks when testing behavior—mocks couple tests to implementation and break on every refactor.
# CORRECT: Fake tests behavior (use this approach)
def test_user_creation():
fake_db = FakeDatabaseAdapter()
service = UserService(database=fake_db)
user = service.create_user("alice@example.com")
assert user.id == 1
assert "INSERT" in fake_db.executed_queries[0]
# WRONG: Mock couples to implementation—this pattern causes test breakage every time you refactor
def test_user_creation(mocker):
mock_db = mocker.patch("myapp.service.database")
# Breaks on refactor
Factory Fixtures
@pytest.fixture
def make_user():
def _make(name="test", **kwargs):
return User(name=name, email=f"{name}@example.com", **kwargs)
return _make
Capture Side Effects
@pytest.fixture
def capture_emails(monkeypatch):
sent = []
monkeypatch.setattr("myapp.email.send", lambda **kw: sent.append(kw))
return sent
YOU MUST
- •Test behavior, not implementation—tests that verify implementation details are technical debt
- •Use fakes for business logic (mocks without fakes = brittle tests that fail on refactoring)
- •Name tests descriptively:
test_<what>_<condition>_<expected> - •Use
tmp_pathfor all file operations in tests—never hardcode paths
NEVER
- •Use subprocess in unit tests—always use CliRunner from click.testing
- •Hardcode paths in tests
- •Test private methods directly (test public API behavior instead)
- •Use
time.sleep()in unit tests (use monkeypatch or freezegun)
References (Load on Demand)
- •references/test-doubles.md - Fakes vs mocks, when to use each
- •references/anti-patterns.md - Common mistakes
- •references/test-layers.md - Five-layer strategy, distribution
- •references/fixture-patterns.md - Factory fixtures, scope, teardown
- •references/agentic-testing.md - AI-assisted test writing