py-testing-async
Async testing patterns with pytest-asyncio. Use when writing tests, mocking async code, testing database operations, or debugging test failures.
What this skill does
# Python Async Testing
## Problem Statement
Async testing requires specific patterns. pytest-asyncio has modes that affect behavior. Database tests need isolation. Mocking async functions differs from sync. Get these wrong and tests are flaky or don't catch bugs.
---
## Pattern: pytest-asyncio Configuration
**Problem:** Pytest needs configuration for async tests.
```toml
# pyproject.toml
[tool.pytest.ini_options]
asyncio_mode = "strict" # Requires explicit @pytest.mark.asyncio
# OR
asyncio_mode = "auto" # All async tests run automatically
```
```python
import pytest
# With asyncio_mode = "strict" (this codebase)
@pytest.mark.asyncio
async def test_something():
result = await some_async_function()
assert result == expected
# Without the marker = test won't run as async!
```
---
## Pattern: Async Fixtures
**Problem:** Fixtures that provide async resources need specific handling.
```python
import pytest
from sqlalchemy.ext.asyncio import AsyncSession
# ✅ CORRECT: Async fixture for session
@pytest.fixture
async def session() -> AsyncGenerator[AsyncSession, None]:
async with async_session() as session:
yield session
await session.rollback() # Clean up after test
# ✅ CORRECT: Async fixture for test data
@pytest.fixture
async def test_user(session: AsyncSession) -> User:
user = User(email="[email protected]", hashed_password="...")
session.add(user)
await session.commit()
await session.refresh(user)
return user
# ✅ CORRECT: Using async fixtures
@pytest.mark.asyncio
async def test_get_user(session: AsyncSession, test_user: User):
result = await session.execute(
select(User).where(User.id == test_user.id)
)
user = result.scalar_one()
assert user.email == "[email protected]"
```
---
## Pattern: Database Test Isolation
**Problem:** Tests polluting each other's database state.
```python
# ✅ CORRECT: Transaction rollback per test
@pytest.fixture
async def session() -> AsyncGenerator[AsyncSession, None]:
async with async_session() as session:
# Start a transaction
async with session.begin():
yield session
# Rollback happens automatically when we exit
# ✅ CORRECT: Nested transactions for complex tests
@pytest.fixture
async def session() -> AsyncGenerator[AsyncSession, None]:
async with async_session() as session:
await session.begin()
yield session
await session.rollback()
# Alternative: Use separate test database
# conftest.py
@pytest.fixture(scope="session")
def event_loop():
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
@pytest.fixture(scope="session")
async def test_engine():
# Use SQLite for tests, PostgreSQL for prod
engine = create_async_engine("sqlite+aiosqlite:///:memory:")
async with engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)
yield engine
await engine.dispose()
```
---
## Pattern: Mocking Async Functions
**Problem:** Regular `Mock` doesn't work with async functions.
```python
from unittest.mock import AsyncMock, patch
# ✅ CORRECT: AsyncMock for async functions
@pytest.mark.asyncio
async def test_with_mocked_service():
mock_service = AsyncMock()
mock_service.get_user.return_value = User(id=uuid4(), email="[email protected]")
result = await mock_service.get_user(user_id)
assert result.email == "[email protected]"
mock_service.get_user.assert_called_once_with(user_id)
# ✅ CORRECT: Patching async functions
@pytest.mark.asyncio
@patch("app.services.user_service.send_email", new_callable=AsyncMock)
async def test_user_creation_sends_email(mock_send_email: AsyncMock, session: AsyncSession):
mock_send_email.return_value = True
user = await create_user(email="[email protected]", session=session)
mock_send_email.assert_called_once_with(user.email, "Welcome!")
# ✅ CORRECT: AsyncMock with side_effect
mock_service = AsyncMock()
mock_service.get_user.side_effect = [
User(id=uuid4(), email="[email protected]"),
User(id=uuid4(), email="[email protected]"),
]
# First call returns first user, second call returns second user
```
---
## Pattern: HTTP Client Testing
**Problem:** Testing FastAPI endpoints with async client.
```python
import pytest
from httpx import AsyncClient, ASGITransport
from app.main import app
@pytest.fixture
async def client() -> AsyncGenerator[AsyncClient, None]:
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test",
) as client:
yield client
@pytest.mark.asyncio
async def test_get_users(client: AsyncClient):
response = await client.get("/api/users")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
@pytest.mark.asyncio
async def test_create_assessment(client: AsyncClient, auth_headers: dict):
response = await client.post(
"/api/assessments",
json={"title": "Test Assessment", "skill_areas": ["fundamentals"]},
headers=auth_headers,
)
assert response.status_code == 201
data = response.json()
assert data["title"] == "Test Assessment"
# ✅ CORRECT: Auth fixture
@pytest.fixture
async def auth_headers(test_user: User) -> dict:
token = create_access_token(user_id=test_user.id)
return {"Authorization": f"Bearer {token}"}
```
---
## Pattern: Testing Service Functions
```python
@pytest.mark.asyncio
async def test_calculate_rating(session: AsyncSession, test_user: User):
# Arrange: Create test data
assessment = Assessment(user_id=test_user.id, title="Test")
session.add(assessment)
await session.commit()
answers = [
UserAnswer(user_id=test_user.id, question_id=q_id, value=4)
for q_id in question_ids
]
session.add_all(answers)
await session.commit()
# Act: Call the service
result = await calculate_rating(assessment.id, session)
# Assert: Check the result
assert result.rating >= 1.0
assert result.rating <= 5.5
assert result.confidence > 0
@pytest.mark.asyncio
async def test_calculate_rating_no_answers(session: AsyncSession, test_user: User):
assessment = Assessment(user_id=test_user.id, title="Empty")
session.add(assessment)
await session.commit()
# Should raise or return specific result
with pytest.raises(ValueError, match="No answers found"):
await calculate_rating(assessment.id, session)
```
---
## Pattern: Testing Multi-Step Flows
Same principle as frontend - test entire flows, not just units:
```python
@pytest.mark.asyncio
async def test_complete_assessment_flow(session: AsyncSession, test_user: User):
"""Test full assessment flow: create -> answer -> submit -> results."""
# Step 1: Create assessment
assessment = await create_assessment(
user_id=test_user.id,
data=AssessmentCreate(title="Full Flow Test", skill_areas=["fundamentals"]),
session=session,
)
assert assessment.id is not None
# Step 2: Answer questions
questions = await get_assessment_questions(assessment.id, session)
for question in questions:
await submit_answer(
user_id=test_user.id,
question_id=question.id,
value=4,
session=session,
)
# Step 3: Submit assessment
result = await submit_assessment(assessment.id, session)
assert result.status == "completed"
# Step 4: Verify results
rating = await get_assessment_rating(assessment.id, session)
assert rating is not None
assert rating.skill_area == "fundamentals"
```
---
## Pattern: Fixture Scopes
**Problem:** Understanding when fixtures are recreated.
```python
# function (default) - recreated for each test
@pytest.fixture
async def session():
... # New session per test
# class - shared within test class
@pytest.fixture(scope="class")
asynRelated in Writing & Docs
jax-development
IncludedUse this skill when the user is writing, debugging, profiling, refactoring, reviewing, benchmarking, parallelising, exporting, or explaining JAX code, or when they mention JAX, jax.numpy, jit, grad, value_and_grad, vmap, scan, lax, random keys, pytrees, jax.Array, sharding, Mesh, PartitionSpec, NamedSharding, pmap, shard_map, Pallas, XLA, StableHLO, checkify, profiler, or the JAX repo. It helps turn NumPy or PyTorch-style code into pure functional JAX, fix tracer/control-flow/shape/PRNG bugs, remove recompiles and host-device syncs, choose transforms and sharding strategies, inspect jaxpr/lowering/IR, and benchmark compiled code correctly.
nature-article-writer
IncludedDrafts, rewrites, diagnostically critiques, and style-calibrates primary research manuscripts for Nature and Nature Portfolio journals. Use when the user wants a Nature-style title, summary paragraph or abstract, introduction, results, discussion, methods, figure legends, presubmission enquiry, cover letter, reviewer response, or when a scientific draft sounds generic, jargon-heavy, structurally weak, or AI-ish and needs precise, broad-reader-friendly prose without inventing data, analyses, or references. Best for primary research articles and letters rather than reviews or press releases unless explicitly adapting one.
deckrd
IncludedDocument-driven framework that derives requirements, specifications, implementation plans, and executable tasks from goals through structured AI dialogue. Use when user says "write requirements", "create spec", "plan implementation", "derive tasks", "structure this feature", "break down into tasks", or "document this module". Also use for reverse engineering existing code into docs (/deckrd rev). Do NOT use for direct code writing — use /deckrd-coder after tasks are generated. Do NOT use when the user only wants to run or fix existing code without planning.
clinical-decision-support
IncludedGenerate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug development, clinical research, and evidence synthesis.
handling-sf-data
IncludedSalesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use querying-soql), Apex test execution (use running-apex-tests), or metadata deployment (use deploying-metadata).
accelint-ac-to-playwright
IncludedConvert and validate acceptance criteria for Playwright test automation. Use when user asks to (1) review/evaluate/check if AC are ready for automation, (2) assess if AC can be converted as-is, (3) validate AC quality for Playwright, (4) turn AC into tests, (5) generate tests from acceptance criteria, (6) convert .md bullets or .feature Gherkin files to Playwright specs, (7) create test automation from requirements. Handles both bullet-style markdown and Gherkin syntax with JSON test plan generation and validation.