Awesome-omni-skill testing-strategy-python
Python/FastAPI/Django testing conventions. pytest, fixtures, httpx, TestClient, factory_boy. Use when writing or reviewing Python tests.
install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/testing-strategy-python" ~/.claude/skills/diegosouzapw-awesome-omni-skill-testing-strategy-python && rm -rf "$T"
manifest:
skills/development/testing-strategy-python/SKILL.mdsource content
Python testing conventions for FastAPI, Django, and Flask.
Frameworks
- Unit/Integration tests: pytest (always — never unittest style)
- API tests:
(FastAPI) orhttpx.AsyncClientTestClient - Mocking:
(wrapper aroundpytest-mock
)unittest.mock - Fixtures: pytest fixtures (not setUp/tearDown)
- Test data:
or plain fixture functionsfactory_boy
Unit Tests
import pytest from unittest.mock import AsyncMock from app.services.order_service import OrderService @pytest.fixture def mock_repo(): repo = AsyncMock() repo.find_by_id.return_value = {"id": 1, "status": "PENDING"} repo.save.return_value = {"id": 1, "status": "PENDING"} return repo @pytest.fixture def service(mock_repo): return OrderService(repository=mock_repo) async def test_create_order_sets_pending_status(service, mock_repo): result = await service.create_order(product_id=1, quantity=2) assert result["status"] == "PENDING" mock_repo.save.assert_called_once() async def test_find_order_raises_when_not_found(service, mock_repo): mock_repo.find_by_id.return_value = None with pytest.raises(OrderNotFoundError): await service.find_by_id(999)
Rules
- Use
for all setup — never class-basedpytest.fixture
/setUptearDown - Use
for async functions,AsyncMock
for syncMagicMock - Fixtures compose: small fixtures combine into larger ones
- Use
for shared fixtures across test modulesconftest.py
FastAPI API Tests
import pytest from httpx import AsyncClient, ASGITransport from app.main import app @pytest.fixture async def client(): transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as client: yield client async def test_get_orders_returns_list(client): response = await client.get("/api/orders", headers={"Authorization": "Bearer test-token"}) assert response.status_code == 200 assert isinstance(response.json(), list) async def test_create_order_validates_input(client): response = await client.post("/api/orders", json={"quantity": -1}) assert response.status_code == 422 assert "quantity" in response.json()["detail"][0]["loc"] async def test_get_order_returns_404_when_not_found(client): response = await client.get("/api/orders/999") assert response.status_code == 404
Rules
- Use
withhttpx.AsyncClient
for async FastAPIASGITransport - Use
from Starlette for sync tests (simpler)TestClient - Override dependencies with
app.dependency_overrides[get_db] = mock_db - Test status codes, response body, validation errors
Django Tests
import pytest from django.test import Client from app.models import Order @pytest.fixture def api_client(): return Client() @pytest.mark.django_db def test_list_orders(api_client): Order.objects.create(status="PENDING") response = api_client.get("/api/orders/") assert response.status_code == 200 assert len(response.json()) == 1 @pytest.mark.django_db def test_create_order(api_client): response = api_client.post("/api/orders/", data={"product_id": 1}, content_type="application/json") assert response.status_code == 201 assert Order.objects.count() == 1
Rules
- Mark DB tests with
@pytest.mark.django_db - Use
pluginpytest-django - Use
frombaker.make(Order)
for test datamodel_bakery - Transaction rollback is automatic per test
Fixtures Pattern (conftest.py)
# tests/conftest.py import pytest from app.database import get_test_db @pytest.fixture(scope="session") def db_engine(): """Create test database once per session.""" engine = create_engine("sqlite:///test.db") Base.metadata.create_all(engine) yield engine Base.metadata.drop_all(engine) @pytest.fixture def db_session(db_engine): """Fresh DB session per test with rollback.""" connection = db_engine.connect() transaction = connection.begin() session = Session(bind=connection) yield session session.close() transaction.rollback() connection.close() @pytest.fixture def sample_order(db_session): order = Order(status="PENDING", product_id=1) db_session.add(order) db_session.commit() return order
File Structure
tests/ ├── conftest.py Shared fixtures ├── unit/ │ ├── test_order_service.py │ └── test_payment_service.py ├── api/ │ ├── test_orders_api.py │ └── test_auth_api.py └── integration/ └── test_order_workflow.py
Naming
- File:
(pytest auto-discoverstest_module_name.py
prefix)test_ - Function:
ortest_should_behavior_when_conditiontest_behavior_description - No classes needed — plain functions with fixtures