pytest-coder
Write pytest tests with fixtures, parametrization, mocking, async testing, and modern patterns. Use when creating or updating Python test files. Not for unittest — use standard library patterns instead.
What this skill does
# Pytest Coder
## Core Philosophy
| Principle | Application |
|-----------|-------------|
| **AAA Pattern** | Arrange-Act-Assert for every test |
| **Behavior over Implementation** | Test what code does, not how |
| **Isolation** | Tests must be independent |
| **Fast Tests** | Mock I/O, minimize database hits |
| **Descriptive Names** | Test name explains the scenario |
| **Coverage** | Test happy paths AND edge cases |
## Project Structure
```
tests/
├── conftest.py # Shared fixtures
├── unit/ # Unit tests (fast, isolated)
│ ├── test_models.py
│ └── test_services.py
├── integration/ # Integration tests (real dependencies)
│ └── test_api.py
└── fixtures/ # Test data files
└── sample_data.json
```
## Essential Patterns
### Basic Test Structure
```python
import pytest
from myapp.services import UserService
class TestUserService:
"""Tests for UserService."""
def test_create_user_with_valid_data(self, user_service):
# Arrange
user_data = {"email": "[email protected]", "name": "Test User"}
# Act
result = user_service.create(user_data)
# Assert
assert result.email == "[email protected]"
assert result.id is not None
def test_create_user_with_duplicate_email_raises_error(self, user_service, existing_user):
# Arrange
user_data = {"email": existing_user.email, "name": "Another User"}
# Act & Assert
with pytest.raises(ValueError, match="Email already exists"):
user_service.create(user_data)
```
### Fixtures
```python
# conftest.py
import pytest
from myapp.database import get_db
from myapp.services import UserService
@pytest.fixture
def db():
"""Provide a clean database session."""
session = get_db()
yield session
session.rollback()
@pytest.fixture
def user_service(db):
"""Provide UserService instance."""
return UserService(db)
@pytest.fixture
def sample_user():
"""Provide sample user data."""
return {"email": "[email protected]", "name": "Test User", "password": "secret123"}
@pytest.fixture
def existing_user(db, sample_user):
"""Create and return an existing user."""
from myapp.models import User
user = User(**sample_user)
db.add(user)
db.commit()
return user
```
### Parametrized Tests
```python
import pytest
@pytest.mark.parametrize("input_email,expected_valid", [
("[email protected]", True),
("[email protected]", True),
("invalid-email", False),
("missing@domain", False),
("", False),
])
def test_email_validation(input_email, expected_valid):
from myapp.validators import is_valid_email
assert is_valid_email(input_email) == expected_valid
@pytest.mark.parametrize("status,expected_message", [
("pending", "Order is being processed"),
("shipped", "Order has been shipped"),
("delivered", "Order has been delivered"),
], ids=["pending-status", "shipped-status", "delivered-status"])
def test_order_status_message(status, expected_message):
from myapp.orders import get_status_message
assert get_status_message(status) == expected_message
```
### Mocking
```python
from unittest.mock import Mock, patch, AsyncMock
def test_send_email_calls_smtp(user_service):
# Mock external dependency
with patch("myapp.services.smtp_client") as mock_smtp:
mock_smtp.send.return_value = True
user_service.send_welcome_email("[email protected]")
mock_smtp.send.assert_called_once_with(
to="[email protected]",
subject="Welcome!",
)
def test_payment_processing_handles_failure():
mock_gateway = Mock()
mock_gateway.charge.side_effect = PaymentError("Card declined")
service = PaymentService(gateway=mock_gateway)
with pytest.raises(PaymentError):
service.process_payment(amount=100)
```
### Async Testing
```python
import pytest
@pytest.mark.asyncio
async def test_async_fetch_user(user_service):
# Arrange
user_id = 1
# Act
user = await user_service.get_by_id(user_id)
# Assert
assert user.id == user_id
@pytest.fixture
async def async_db():
"""Async database session fixture."""
from myapp.database import async_session
async with async_session() as session:
yield session
await session.rollback()
# Mock async functions
@pytest.mark.asyncio
async def test_async_external_api():
with patch("myapp.client.fetch_data", new_callable=AsyncMock) as mock_fetch:
mock_fetch.return_value = {"status": "ok"}
result = await fetch_and_process()
assert result["status"] == "ok"
```
### Testing Exceptions
```python
import pytest
def test_divide_by_zero_raises_error():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
def test_invalid_input_raises_with_message():
with pytest.raises(ValueError, match="must be positive"):
process_amount(-100)
def test_exception_attributes():
with pytest.raises(CustomError) as exc_info:
risky_operation()
assert exc_info.value.code == "E001"
assert "failed" in str(exc_info.value)
```
## Fixture Scopes
| Scope | Lifecycle | Use Case |
|-------|-----------|----------|
| `function` | Per test (default) | Most fixtures |
| `class` | Per test class | Shared setup within class |
| `module` | Per module | Expensive setup shared by module |
| `session` | Entire test run | Database connections, servers |
```python
@pytest.fixture(scope="session")
def database_engine():
"""Create engine once for entire test session."""
engine = create_engine(TEST_DATABASE_URL)
yield engine
engine.dispose()
@pytest.fixture(scope="function")
def db_session(database_engine):
"""Create fresh session per test."""
connection = database_engine.connect()
transaction = connection.begin()
session = Session(bind=connection)
yield session
session.close()
transaction.rollback()
connection.close()
```
## Markers
```python
# pytest.ini or pyproject.toml
[tool.pytest.ini_options]
markers = [
"slow: marks tests as slow",
"integration: marks integration tests",
"unit: marks unit tests",
]
# Usage
@pytest.mark.slow
def test_complex_calculation():
...
@pytest.mark.integration
def test_database_connection():
...
# Run specific markers
# pytest -m "not slow"
# pytest -m "unit"
```
## Quality Checklist
- [ ] AAA pattern (Arrange-Act-Assert) in every test
- [ ] Descriptive test names explaining the scenario
- [ ] Fixtures for common setup
- [ ] Parametrized tests for multiple inputs
- [ ] Mocks for external dependencies
- [ ] Happy path tested
- [ ] Error cases tested
- [ ] Edge cases covered
- [ ] Async tests use `@pytest.mark.asyncio`
- [ ] No test interdependencies
- [ ] Coverage >90%
## Anti-Patterns
| Anti-Pattern | Why Bad | Fix |
|--------------|---------|-----|
| Tests depend on order | Flaky, hard to debug | Use fixtures, isolate |
| Testing implementation | Brittle tests | Test behavior |
| Too many assertions | Hard to identify failure | One assertion per test |
| No error case tests | Missing coverage | Test exceptions explicitly |
| Slow unit tests | Slow feedback | Mock I/O, use in-memory DB |
Related in Code Review
gstack
IncludedFast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack)
startup-due-diligence
IncludedLegal due diligence review for seed-stage and Series A startups (US, Delaware C-Corp focus). Supports both investor and founder perspectives. Capabilities include: (1) Interactive document review and issue spotting; (2) Document request list generation; (3) Cap table and SAFE/convertible note analysis; (4) Red flag identification with severity ratings; (5) Diligence report generation. TRIGGERS: due diligence, DD, startup investment, cap table review, Series A, seed round, investor diligence, legal review startup, SAFE analysis, convertible note, 409A, founder vesting.
interview-master
IncludedThis skill should be used when the user asks to "generate interview questions", "prepare for interview", "optimize resume", "conduct mock interview", "analyze git commits for resume", "generate resume from code", "review my resume", or mentions interview preparation, career assistance, or extracting project experience from git history. Provides comprehensive interview and career development guidance for both job seekers and interviewers.
fix-issue
IncludedFixes GitHub issues using parallel analysis agents for root cause investigation, code exploration, and regression detection. Reads issue context from gh CLI, searches codebase and memory for related patterns, generates a fix with tests, and links the resolution back to the issue via PR. Includes prevention analysis to avoid recurrence. Use when debugging errors, resolving regressions, fixing bugs, or triaging issues.
sf-apex
IncludedGenerates and reviews Salesforce Apex code with 150-point scoring. TRIGGER when: user writes, reviews, or fixes Apex classes, triggers, test classes, batch/queueable/schedulable jobs, or touches .cls/.trigger files. DO NOT TRIGGER when: LWC JavaScript (use sf-lwc), Flow XML (use sf-flow), SOQL-only queries (use sf-soql), or non-Salesforce code.
swift-development
IncludedComprehensive Swift development for building, testing, and deploying iOS/macOS applications. Use when Claude needs to: (1) Build Swift packages or Xcode projects from command line, (2) Run tests with XCTest or Swift Testing framework, (3) Manage iOS simulators with simctl, (4) Handle code signing, provisioning profiles, and app distribution, (5) Format or lint Swift code with SwiftFormat/SwiftLint, (6) Work with Swift Package Manager (SPM), (7) Implement Swift 6 concurrency patterns (async/await, actors, Sendable), (8) Create SwiftUI views with MVVM architecture, (9) Set up Core Data or SwiftData persistence, or any other Swift/iOS/macOS development tasks.