
Python Testing Patterns
Level up pytest coverage for async services, env monkeypatching, temp files, and CI-ready suites on Python backends.
Overview
Python Testing Patterns is an agent skill most often used in Ship (also Build/backend) that documents advanced pytest patterns for async code, monkeypatching, fixtures, and CI-ready Python suites.
Install
npx skills add https://github.com/wshobson/agents --skill python-testing-patternsWhat is this skill?
- Pattern 6: pytest-asyncio tests, asyncio.gather concurrency, and async fixtures with teardown
- Pattern 7: os.environ monkeypatch patterns for config and secrets in unit tests
- conftest setup, temporary files, and isolated test environments
- Property-based testing and database testing patterns in the advanced reference
- CI/CD integration and configuration guidance for repeatable pytest runs
- Documents Pattern 6 (async) and Pattern 7 (monkeypatch) as numbered advanced patterns in the reference
Adoption & trust: 22.5k installs on skills.sh; 36.5k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your Python service has brittle or missing tests for async paths, environment config, and databases, so every ship feels manual and risky.
Who is it for?
Solo builders maintaining FastAPI-style services, data scripts, or CLIs who already use pytest and want agent-generated tests that match advanced conventions.
Skip if: Absolute beginners who have not run pytest once, or pure frontend/JavaScript projects with no Python runtime.
When should I use this skill?
You are writing or fixing pytest suites for async Python, environment-dependent config, databases, or CI pipelines.
What do I get? / Deliverables
Structured pytest patterns—async markers, monkeypatch, conftest, and CI hooks—you can drop into the repo and run in pipeline gates.
- New or updated pytest modules using documented patterns
- conftest or CI snippet aligned with project layout
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Formal test patterns belong on the Ship/testing shelf because they harden code before release, though the same patterns are written during Build. Testing is where async markers, fixtures, property-based checks, and DB test harnesses turn prototypes into shippable Python services.
Where it fits
Agent adds asyncio.gather coverage while you implement a new fetch layer in a FastAPI service.
You wire monkeypatch-based DATABASE_URL tests before tagging a release.
PR review asks for conftest cleanup; the skill supplies the advanced fixture layout.
A production bug fix gets a regression test using temp files and isolated DB patterns from the reference.
How it compares
Pattern library for pytest depth, not a one-shot test generator that skips async fixtures and env isolation.
Common Questions / FAQ
Who is python-testing-patterns for?
Indie Python developers and small teams who ship APIs or tools and need consistent async, env, and integration test structure in pytest.
When should I use python-testing-patterns?
During Ship testing before a release, while hardening Build/backend modules, or when CI starts failing on async or environment-dependent tests.
Is python-testing-patterns safe to install?
The skill suggests test code and env monkeypatching only in test contexts; review the Security Audits panel on this page and never point patterns at production credentials.
SKILL.md
READMESKILL.md - Python Testing Patterns
# Python Testing Patterns — Advanced Reference Advanced testing patterns including async code, monkeypatching, temporary files, conftest setup, property-based testing, database testing, CI/CD integration, and configuration. ## Pattern 6: Testing Async Code ```python # test_async.py import pytest import asyncio async def fetch_data(url: str) -> dict: """Fetch data asynchronously.""" await asyncio.sleep(0.1) return {"url": url, "data": "result"} @pytest.mark.asyncio async def test_fetch_data(): """Test async function.""" result = await fetch_data("https://api.example.com") assert result["url"] == "https://api.example.com" assert "data" in result @pytest.mark.asyncio async def test_concurrent_fetches(): """Test concurrent async operations.""" urls = ["url1", "url2", "url3"] tasks = [fetch_data(url) for url in urls] results = await asyncio.gather(*tasks) assert len(results) == 3 assert all("data" in r for r in results) @pytest.fixture async def async_client(): """Async fixture.""" client = {"connected": True} yield client client["connected"] = False @pytest.mark.asyncio async def test_with_async_fixture(async_client): """Test using async fixture.""" assert async_client["connected"] is True ``` ## Pattern 7: Monkeypatch for Testing ```python # test_environment.py import os import pytest def get_database_url() -> str: """Get database URL from environment.""" return os.environ.get("DATABASE_URL", "sqlite:///:memory:") def test_database_url_default(): """Test default database URL.""" # Will use actual environment variable if set url = get_database_url() assert url def test_database_url_custom(monkeypatch): """Test custom database URL with monkeypatch.""" monkeypatch.setenv("DATABASE_URL", "postgresql://localhost/test") assert get_database_url() == "postgresql://localhost/test" def test_database_url_not_set(monkeypatch): """Test when env var is not set.""" monkeypatch.delenv("DATABASE_URL", raising=False) assert get_database_url() == "sqlite:///:memory:" class Config: """Configuration class.""" def __init__(self): self.api_key = "production-key" def get_api_key(self): return self.api_key def test_monkeypatch_attribute(monkeypatch): """Test monkeypatching object attributes.""" config = Config() monkeypatch.setattr(config, "api_key", "test-key") assert config.get_api_key() == "test-key" ``` ## Pattern 8: Temporary Files and Directories ```python # test_file_operations.py import pytest from pathlib import Path def save_data(filepath: Path, data: str): """Save data to file.""" filepath.write_text(data) def load_data(filepath: Path) -> str: """Load data from file.""" return filepath.read_text() def test_file_operations(tmp_path): """Test file operations with temporary directory.""" # tmp_path is a pathlib.Path object test_file = tmp_path / "test_data.txt" # Save data save_data(test_file, "Hello, World!") # Verify file exists assert test_file.exists() # Load and verify data data = load_data(test_file) assert data == "Hello, World!" def test_multiple_files(tmp_path): """Test with multiple temporary files.""" files = { "file1.txt": "Content 1", "file2.txt": "Content 2", "file3.txt": "Content 3" } for filename, content in files.items(): filepath = tmp_path / filename save_data(filepath, content) # Verify all files created assert len(list(tmp_path.iterdir())) == 3 # Verify contents for filename, expected_content in files.items(): filepath = tmp_path / filename assert load_data(filepath) == expected_content ``` ## Pattern 9: Custom Fixtures and Conftest ```python # conftest.py """Shared fixtures for all tests.""" import pytest @pytest.fixture(scope="session") def database_url(): """Provide d