用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/agusmdev/burntop --skill fastapi-testing命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
OpenProse is a programming language for AI sessions. An AI session is a Turing-complete computer; OpenProse structures English into unambiguous control flow. More pattern than framework—it ships as a skill with no dependencies. Activate when: running .prose files, mentioning OpenProse, or orchestrating multi-agent workflows from a script. Use this skill if you ever want to kick off more than one subagent at a time, or orchestrate anything interesting between more than one subagent. Write a .prose file and save it in the .claude-plugin/ directory. Then embody the OpenProse VM, as described in prose.md, and execute it.
Configure Alembic for async SQLAlchemy migrations with PostgreSQL
Create FastAPI application factory with lifespan, middleware, pagination, and router configuration
正在显示 SKILL.md
基于 SOC 职业分类
| name | fastapi-testing |
| description | Configure pytest-asyncio with test database fixtures and integration tests for FastAPI routers |
This skill covers setting up pytest-asyncio for integration testing FastAPI routers with a test database.
Ensure pyproject.toml has:
[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
testpaths = ["tests"]
pythonpath = ["src"]
Update src/app/config.py:
from pydantic import PostgresDsn
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
case_sensitive=False,
)
# ... existing settings ...
# Test database (optional, falls back to modifying database_url)
test_database_url: PostgresDsn | None = None
settings = Settings()
Update .env.example:
# Test database
TEST_DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/dbname_test
Create tests/conftest.py:
import asyncio
from collections.abc import AsyncGenerator, Generator
import pytest
from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import (
AsyncSession,
async_sessionmaker,
create_async_engine,
)
from app.config import settings
from app.core.models import Base
from app.dependencies import get_db
from app.main import app
# Get test database URL
def get_test_database_url() -> str:
"""Get the test database URL."""
if settings.test_database_url:
return str(settings.test_database_url)
# Fallback: modify main database URL to use test database
main_url = str(settings.database_url)
return main_url.replace("/dbname", "/dbname_test")
# Create test engine
test_engine = create_async_engine(
get_test_database_url(),
echo=False,
pool_pre_ping=True,
)
# Create test session factory
test_async_session_factory = async_sessionmaker(
bind=test_engine,
class_=AsyncSession,
expire_on_commit=False,
autocommit=False,
autoflush=False,
)
@pytest.fixture(scope="session")
def event_loop() -> Generator[asyncio.AbstractEventLoop, None, ]:
loop = asyncio.get_event_loop_policy().new_event_loop()
loop
loop.close()
() -> AsyncGenerator[, ]:
app.items.models Item
test_engine.begin() conn:
conn.run_sync(Base.metadata.create_all)
test_engine.begin() conn:
conn.run_sync(Base.metadata.drop_all)
test_engine.dispose()
() -> AsyncGenerator[AsyncSession, ]:
test_async_session_factory() session:
session
session.rollback()
() -> AsyncGenerator[AsyncClient, ]:
() -> AsyncGenerator[AsyncSession, ]:
db_session
app.dependency_overrides[get_db] = override_get_db
AsyncClient(
transport=ASGITransport(app=app),
base_url=,
) ac:
ac
app.dependency_overrides.clear()
() -> :
{
: ,
: ,
}
() -> :
response = client.post(, json=sample_item_data)
response.status_code ==
response.json()
# Empty file
# Empty file
Create tests/api/v1/test_items.py:
import pytest
from httpx import AsyncClient
class TestCreateItem:
"""Tests for POST /api/v1/items"""
async def test_create_item_success(
self,
client: AsyncClient,
sample_item_data: dict,
) -> None:
"""Test successful item creation."""
response = await client.post("/api/v1/items", json=sample_item_data)
assert response.status_code == 201
data = response.json()
assert data["name"] == sample_item_data["name"]
assert data["description"] == sample_item_data["description"]
assert "id" in data
assert "created_at" in data
assert "updated_at" in data
async def test_create_item_duplicate_name(
self,
client: AsyncClient,
created_item: dict,
sample_item_data: dict,
) -> None:
"""Test that creating item with duplicate name fails."""
response = await client.post("/api/v1/items", json=sample_item_data)
assert response.status_code == 409
data = response.json()
data[] ==
() -> :
response = client.post(, json={: })
response.status_code ==
data = response.json()
data[] ==
() -> :
response = client.post(, json={: })
response.status_code ==
:
() -> :
item_id = created_item[]
response = client.get()
response.status_code ==
data = response.json()
data[] == item_id
data[] == created_item[]
() -> :
fake_id =
response = client.get()
response.status_code ==
data = response.json()
data[] ==
() -> :
response = client.get()
response.status_code ==
:
() -> :
response = client.get()
response.status_code ==
data = response.json()
data[] == []
data[] ==
() -> :
response = client.get()
response.status_code ==
data = response.json()
(data[]) >=
data[] >=
() -> :
response = client.get()
response.status_code ==
data = response.json()
data
data
data[] ==
data[] ==
() -> :
name = created_item[]
response = client.get()
response.status_code ==
data = response.json()
(item[] == name item data[])
() -> :
response = client.get()
response.status_code ==
data = response.json()
( item[].lower() item data[])
:
() -> :
item_id = created_item[]
update_data = {: }
response = client.patch(, json=update_data)
response.status_code ==
data = response.json()
data[] ==
data[] == created_item[]
() -> :
item_id = created_item[]
update_data = {: }
response = client.patch(, json=update_data)
response.status_code ==
data = response.json()
data[] == created_item[]
data[] ==
() -> :
fake_id =
response = client.patch(, json={: })
response.status_code ==
:
() -> :
item_id = created_item[]
response = client.delete()
response.status_code ==
get_response = client.get()
get_response.status_code ==
() -> :
fake_id =
response = client.delete()
response.status_code ==
:
() -> :
item_id = created_item[]
client.delete()
response = client.post()
response.status_code ==
data = response.json()
data[] == item_id
get_response = client.get()
get_response.status_code ==
# Run all tests
uv run pytest
# Run with verbose output
uv run pytest -v
# Run specific test file
uv run pytest tests/api/v1/test_items.py
# Run specific test class
uv run pytest tests/api/v1/test_items.py::TestCreateItem
# Run specific test
uv run pytest tests/api/v1/test_items.py::TestCreateItem::test_create_item_success
# Run with coverage
uv run pytest --cov=app --cov-report=html
# Run tests matching a pattern
uv run pytest -k "create"
@pytest.fixture
def item_factory():
"""Factory for creating item data with unique names."""
counter = 0
def _factory(**overrides):
nonlocal counter
counter += 1
defaults = {
"name": f"Item {counter}",
"description": f"Description {counter}",
}
defaults.update(overrides)
return defaults
return _factory
async def test_with_factory(client: AsyncClient, item_factory):
"""Test using factory fixture."""
item1 = await client.post("/api/v1/items", json=item_factory())
item2 = await client.post("/api/v1/items", json=item_factory())
assert item1.json()["name"] != item2.json()["name"]
@pytest.fixture
async def multiple_items(
client: AsyncClient,
item_factory,
) -> list[dict]:
"""Create multiple items for testing."""
items = []
for i in range(5):
response = await client.post("/api/v1/items", json=item_factory())
items.append(response.json())
return items
Before running tests, create the test database:
# PostgreSQL
createdb dbname_test
# Or via psql
psql -c "CREATE DATABASE dbname_test;"