pytest-patterns
Python testing with pytest covering fixtures, parametrization, mocking, and test organization for reliable test suites
What this skill does
# Pytest Patterns - Comprehensive Testing Guide
A comprehensive skill for mastering Python testing with pytest. This skill covers everything from basic test structure to advanced patterns including fixtures, parametrization, mocking, test organization, coverage analysis, and CI/CD integration.
## When to Use This Skill
Use this skill when:
- Writing tests for Python applications (web apps, APIs, CLI tools, libraries)
- Setting up test infrastructure for a new Python project
- Refactoring existing tests to be more maintainable and efficient
- Implementing test-driven development (TDD) workflows
- Creating fixture patterns for database, API, or external service testing
- Organizing large test suites with hundreds or thousands of tests
- Debugging failing tests or improving test reliability
- Setting up continuous integration testing pipelines
- Measuring and improving code coverage
- Writing integration, unit, or end-to-end tests
- Testing async Python code
- Mocking external dependencies and services
## Core Concepts
### What is pytest?
pytest is a mature, full-featured Python testing framework that makes it easy to write simple tests, yet scales to support complex functional testing. It provides:
- **Simple syntax**: Use plain `assert` statements instead of special assertion methods
- **Powerful fixtures**: Modular, composable test setup and teardown
- **Parametrization**: Run the same test with different inputs
- **Plugin ecosystem**: Hundreds of plugins for extended functionality
- **Detailed reporting**: Clear failure messages and debugging information
- **Test discovery**: Automatic test collection following naming conventions
### pytest vs unittest
```python
# unittest (traditional)
import unittest
class TestMath(unittest.TestCase):
def test_addition(self):
self.assertEqual(2 + 2, 4)
# pytest (simpler)
def test_addition():
assert 2 + 2 == 4
```
### Test Discovery Rules
pytest automatically discovers tests by following these conventions:
1. **Test files**: `test_*.py` or `*_test.py`
2. **Test functions**: Functions prefixed with `test_`
3. **Test classes**: Classes prefixed with `Test` (no `__init__` method)
4. **Test methods**: Methods prefixed with `test_` inside Test classes
## Fixtures - The Heart of pytest
### What are Fixtures?
Fixtures provide a fixed baseline for tests to run reliably and repeatably. They handle setup, provide test data, and perform cleanup.
### Basic Fixture Pattern
```python
import pytest
@pytest.fixture
def sample_data():
"""Provides sample data for testing."""
return {"name": "Alice", "age": 30}
def test_data_access(sample_data):
assert sample_data["name"] == "Alice"
assert sample_data["age"] == 30
```
### Fixture Scopes
Fixtures can have different scopes controlling how often they're created:
- **function** (default): Created for each test function
- **class**: Created once per test class
- **module**: Created once per test module
- **package**: Created once per test package
- **session**: Created once per test session
```python
@pytest.fixture(scope="session")
def database_connection():
"""Database connection created once for entire test session."""
conn = create_db_connection()
yield conn
conn.close() # Cleanup after all tests
@pytest.fixture(scope="module")
def api_client():
"""API client created once per test module."""
client = APIClient()
client.authenticate()
yield client
client.logout()
@pytest.fixture # scope="function" is default
def temp_file():
"""Temporary file created for each test."""
import tempfile
f = tempfile.NamedTemporaryFile(mode='w', delete=False)
yield f.name
os.unlink(f.name)
```
### Fixture Dependencies
Fixtures can depend on other fixtures, creating a dependency graph:
```python
@pytest.fixture
def database():
db = Database()
db.connect()
yield db
db.disconnect()
@pytest.fixture
def user_repository(database):
"""Depends on database fixture."""
return UserRepository(database)
@pytest.fixture
def sample_user(user_repository):
"""Depends on user_repository, which depends on database."""
user = user_repository.create(name="Test User")
yield user
user_repository.delete(user.id)
def test_user_operations(sample_user):
"""Uses sample_user fixture (which uses user_repository and database)."""
assert sample_user.name == "Test User"
```
### Autouse Fixtures
Fixtures that run automatically without being explicitly requested:
```python
@pytest.fixture(autouse=True)
def reset_database():
"""Runs before every test automatically."""
clear_database()
seed_test_data()
@pytest.fixture(autouse=True, scope="session")
def configure_logging():
"""Configure logging once for entire test session."""
import logging
logging.basicConfig(level=logging.DEBUG)
```
### Fixture Factories
Fixtures that return functions for creating test data:
```python
@pytest.fixture
def make_user():
"""Factory fixture for creating users."""
users = []
def _make_user(name, email=None):
user = User(name=name, email=email or f"{name}@example.com")
users.append(user)
return user
yield _make_user
# Cleanup all created users
for user in users:
user.delete()
def test_multiple_users(make_user):
user1 = make_user("Alice")
user2 = make_user("Bob", email="[email protected]")
assert user1.name == "Alice"
assert user2.email == "[email protected]"
```
## Parametrization - Testing Multiple Cases
### Basic Parametrization
Run the same test with different inputs:
```python
import pytest
@pytest.mark.parametrize("input_value,expected", [
(2, 4),
(3, 9),
(4, 16),
(5, 25),
])
def test_square(input_value, expected):
assert input_value ** 2 == expected
```
### Multiple Parameters
```python
@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])
def test_combinations(x, y):
"""Runs 4 times: (0,2), (0,3), (1,2), (1,3)."""
assert x < y
```
### Parametrizing with IDs
Make test output more readable:
```python
@pytest.mark.parametrize("test_input,expected", [
pytest.param("3+5", 8, id="addition"),
pytest.param("2*4", 8, id="multiplication"),
pytest.param("10-2", 8, id="subtraction"),
])
def test_eval(test_input, expected):
assert eval(test_input) == expected
# Output:
# test_eval[addition] PASSED
# test_eval[multiplication] PASSED
# test_eval[subtraction] PASSED
```
### Parametrizing Fixtures
Create fixture instances with different values:
```python
@pytest.fixture(params=["mysql", "postgresql", "sqlite"])
def database_type(request):
"""Test runs three times, once for each database."""
return request.param
def test_database_connection(database_type):
conn = connect_to_database(database_type)
assert conn.is_connected()
```
### Combining Parametrization and Marks
```python
@pytest.mark.parametrize("test_input,expected", [
("[email protected]", True),
("invalid-email", False),
pytest.param("edge@case", True, marks=pytest.mark.xfail),
pytest.param("[email protected]", True, marks=pytest.mark.slow),
])
def test_email_validation(test_input, expected):
assert is_valid_email(test_input) == expected
```
### Indirect Parametrization
Pass parameters through fixtures:
```python
@pytest.fixture
def database(request):
"""Create database based on parameter."""
db_type = request.param
db = Database(db_type)
db.connect()
yield db
db.close()
@pytest.mark.parametrize("database", ["mysql", "postgres"], indirect=True)
def test_database_operations(database):
"""database fixture receives the parameter value."""
assert database.is_connected()
database.execute("SELECT 1")
```
## Mocking and Monkeypatching
### Using pytest's monkeypatch
The `monkeypatch` fixture provides safe patching that's automatically undone:
```python
def test_get_user_env(monkeypatch):
"""Test envRelated 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.