用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/agusmdev/fullstack-ai-template --skill fastapi-testing命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Codebase health scanner and technical debt tracker. Use when the user asks about code quality, technical debt, dead code, large files, god classes, duplicate functions, code smells, naming issues, import cycles, or coupling problems. Also use when asked for a health score, what to fix next, or to create a cleanup plan. Supports 29 languages.
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;"