python-testing
Python testing strategy and patterns. Use when designing test strategy, writing tests, or evaluating test coverage. Covers test pyramid, TDD workflow, and pytest patterns.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install alicoding-nextura-python-testing
Repository
Skill path: .claude/skills/python-testing
Python testing strategy and patterns. Use when designing test strategy, writing tests, or evaluating test coverage. Covers test pyramid, TDD workflow, and pytest patterns.
Open repositoryBest for
Primary workflow: Write Technical Docs.
Technical facets: Full Stack, Tech Writer, Testing.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: alicoding.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install python-testing into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/alicoding/nextura before adding python-testing to shared team environments
- Use python-testing for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: python-testing
scope: python
description: >
Python testing strategy and patterns. Use when designing test strategy,
writing tests, or evaluating test coverage. Covers test pyramid,
TDD workflow, and pytest patterns.
version: 3.0.0
triggers:
- test strategy
- what to test
- test pyramid
- unit test
- integration test
- test coverage
gates:
pre:
- "test -d tests/unit"
- "test -d tests/integration"
post:
- "pytest --cov=src --cov-fail-under=80"
---
# Python Testing
Design test strategies that catch bugs without slowing development. Focus on testing behavior, not implementation.
---
## Quick Start
1. **Load coverage thresholds from SSOT**
2. **Follow test pyramid** (70% unit, 20% integration, 10% E2E)
3. **Write test first** (TDD: Red -> Green -> Refactor)
4. **Run pytest with coverage**
---
## Load Standards from SSOT
```bash
# Load coverage thresholds and test requirements
cat data/enterprise/standards/STD-TEST-001.yaml
```
Apply coverage targets from standards.
---
## Test Pyramid
```
/\ E2E (10%)
/ \ - Critical user journeys
/----\ - Slow, few
/ \
/--------\ Integration (20%)
/ \ - Adapter tests
/------------\ - Real DB/API
/ \
/----------------\ Unit (70%)
- Business logic
- Fast, many
```
**For detailed patterns:** See [reference/pyramid.md](reference/pyramid.md)
---
## TDD Workflow
```
RED -> Write failing test
GREEN -> Write minimum code to pass
REFACTOR -> Improve code, tests still pass
```
```python
# 1. RED: Write failing test
def test_calculate_discount():
order = Order(total=Decimal("100.00"))
assert calculate_discount(order) == Decimal("10.00")
# 2. GREEN: Minimal implementation
def calculate_discount(order: Order) -> Decimal:
return order.total * Decimal("0.10")
# 3. REFACTOR: Improve while tests pass
```
---
## Test Organization
```
tests/
├── conftest.py # Shared fixtures
├── unit/ # Fast, isolated
│ ├── conftest.py
│ └── test_services.py
├── integration/ # With real adapters
│ └── test_database.py
└── e2e/ # Full system
└── test_workflows.py
```
---
## STOP GATES
### STOP GATE 1: Pyramid Balanced
**Check:** Does test distribution match pyramid?
**Pass:** Unit > Integration > E2E by count
**Fail:** STOP. Add more unit tests.
### STOP GATE 2: Test First
**Check:** Did I write the test first?
**Pass:** Test exists and fails before implementation
**Fail:** STOP. Write the test first.
### STOP GATE 3: Coverage Threshold
**Check:** Does coverage meet SSOT requirements?
**Pass:** Meets threshold from STD-TEST-001
**Fail:** STOP. Add tests for uncovered code.
### STOP GATE 4: Fast Tests
**Check:** Are unit tests fast?
**Pass:** Unit suite < 30 seconds
**Fail:** STOP. Fix slow tests.
---
## Quick Reference
```
UNIT TEST (70%)
[ ] Business logic
[ ] Validation rules
[ ] Pure functions
[ ] < 10ms each
INTEGRATION TEST (20%)
[ ] Database adapters
[ ] HTTP clients
[ ] File system
[ ] < 1s each
E2E TEST (10%)
[ ] Critical user journeys
[ ] Login/checkout flows
[ ] < 30s each
```
---
## Anti-Patterns
| If you're doing... | STOP. Do this instead... |
|--------------------|--------------------------|
| More E2E than unit | Invert the pyramid |
| Testing implementation | Test behavior |
| 100% coverage goal | Target by component |
| Slow unit tests | Remove I/O, use fakes |
| Hardcoding thresholds | Load from STD-TEST-001 |
| Code before test | Write failing test first |
---
## Reference Files
- [reference/pyramid.md](reference/pyramid.md) - Test pyramid details
- [reference/patterns.md](reference/patterns.md) - Test patterns and fixtures
- [reference/doubles.md](reference/doubles.md) - Mocks, stubs, fakes
- [reference/advanced.md](reference/advanced.md) - Property testing, CI
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### reference/pyramid.md
```markdown
# Test Pyramid Details
## What Each Level Tests
```
UNIT TESTS: Domain Logic
├── Entities and value objects
├── Domain services
├── Business rules
├── Pure functions
└── Input validation
INTEGRATION TESTS: Boundaries
├── Database adapters (real DB)
├── HTTP clients (mocked or real)
├── File system operations
├── Message queue adapters
└── Third-party API clients
E2E TESTS: User Journeys
├── Critical paths (login, checkout)
├── Happy path workflows
├── Multi-step processes
└── UI interactions
```
---
## Unit Tests
**What to test:**
```python
# Business logic
def calculate_discount(order: Order, customer: Customer) -> Decimal:
if customer.is_premium:
return order.total * Decimal("0.10")
return Decimal("0")
# Validation rules
def validate_email(email: str) -> bool:
return bool(re.match(r"^[\w.-]+@[\w.-]+\.\w+$", email))
```
**What NOT to test:**
- Simple getters/setters
- Data containers (@dataclass)
- Thin wrappers
---
## Integration Tests
```python
def test_user_repository_saves_and_retrieves():
repo = PostgresUserRepository(connection)
user = User(id="1", name="Alice")
repo.save(user)
retrieved = repo.get("1")
assert retrieved == user
```
---
## E2E Tests
```python
def test_checkout_flow():
client.login("[email protected]", "password")
client.add_to_cart(product_id="123", quantity=2)
order = client.checkout(payment_method="card")
assert order.status == "confirmed"
```
| Journey | E2E? | Reason |
|---------|------|--------|
| Login | Yes | Critical |
| Checkout | Yes | Revenue |
| Profile update | No | Unit test |
```
### reference/patterns.md
```markdown
# Test Patterns
## Arrange-Act-Assert
```python
def test_premium_customer_gets_discount():
# Arrange
order = Order(total=Decimal("100.00"))
customer = Customer(is_premium=True)
# Act
discount = calculate_discount(order, customer)
# Assert
assert discount == Decimal("10.00")
```
---
## Parameterized Tests
```python
@pytest.mark.parametrize("email,expected", [
("[email protected]", True),
("invalid", False),
("@nodomain.com", False),
])
def test_email_validation(email: str, expected: bool):
assert validate_email(email) == expected
```
---
## Fixtures
```python
# conftest.py
@pytest.fixture
def order_repository() -> OrderRepository:
return InMemoryOrderRepository()
@pytest.fixture
def order_service(order_repository: OrderRepository) -> OrderService:
return OrderService(repository=order_repository)
@pytest.fixture
def sample_order() -> Order:
return Order(id="order-123", total=Decimal("100.00"))
```
---
## Fixture Scopes
```python
@pytest.fixture(scope="function") # Default: new for each test
def user(): return User()
@pytest.fixture(scope="module") # Shared within module
def database(): return create_db()
@pytest.fixture(scope="session") # Shared across all tests
def app(): return create_app()
```
---
## Factory Pattern
```python
class OrderFactory:
@staticmethod
def build(
id: str | None = None,
total: Decimal = Decimal("100.00"),
) -> Order:
return Order(
id=id or f"order-{uuid.uuid4().hex[:6]}",
total=total,
)
# Usage
def test_discount():
order = OrderFactory.build(total=Decimal("200.00"))
```
```
### reference/doubles.md
```markdown
# Test Doubles
## Types
| Type | Purpose | Example |
|------|---------|---------|
| **Stub** | Return fixed values | `stub.get() -> User("test")` |
| **Mock** | Verify interactions | `mock.send.assert_called()` |
| **Fake** | Working implementation | In-memory DB |
| **Spy** | Record calls | Log then call real |
---
## Stub
```python
def test_discount_for_premium(stub_customer_service):
stub_customer_service.is_premium.return_value = True
discount = calculate_discount(order, stub_customer_service)
assert discount > 0
```
---
## Mock
```python
def test_email_sent_on_signup(mock_email_service):
signup(user, mock_email_service)
mock_email_service.send.assert_called_once_with(
to=user.email,
template="welcome"
)
```
---
## Fake
```python
class InMemoryUserRepository:
def __init__(self):
self._users = {}
def save(self, user: User) -> None:
self._users[user.id] = user
def get(self, id: str) -> User:
return self._users[id]
def test_repository():
repo = InMemoryUserRepository() # Fake
repo.save(user)
assert repo.get(user.id) == user
```
---
## When to Use Each
```
MOCK when:
├── Testing side effects
├── Verifying interactions
└── External service
FAKE when:
├── Need realistic behavior
├── Stateful operations
└── Multiple tests share setup
```
```
### reference/advanced.md
```markdown
# Advanced Testing
## Property-Based Testing
```python
from hypothesis import given, strategies as st
@given(st.lists(st.integers()))
def test_sort_idempotent(lst):
assert sorted(sorted(lst)) == sorted(lst)
@given(st.from_type(User))
def test_json_roundtrip(user):
restored = User.from_json(user.to_json())
assert restored == user
```
---
## Async Testing
```python
@pytest.mark.asyncio
async def test_async_fetch():
async with AsyncClient() as client:
response = await client.get("https://api.example.com")
assert response.status_code == 200
@pytest.fixture
async def async_client():
async with AsyncClient(app=app) as client:
yield client
```
---
## Testcontainers
```python
from testcontainers.postgres import PostgresContainer
@pytest.fixture(scope="session")
def postgres():
with PostgresContainer("postgres:15") as pg:
yield pg.get_connection_url()
def test_with_real_db(postgres):
repo = UserRepository(postgres)
repo.save(User(id="1", name="Alice"))
```
---
## CI Integration
```yaml
# .github/workflows/test.yml
- name: Run tests
run: pytest --cov=src --cov-report=xml --cov-fail-under=80
- name: Upload coverage
uses: codecov/codecov-action@v4
```
---
## Parallel Execution
```bash
# pytest-xdist
pytest -n auto # All CPUs
pytest -n 4 # 4 workers
```
---
## Coverage Targets
Load from SSOT: `STD-TEST-001.yaml`
Typical targets:
| Component | Target |
|-----------|--------|
| Core/domain | 95%+ |
| Services | 85%+ |
| Adapters | 70%+ |
| CLI | 30%+ |
```