Files
voidea/template/docs/api-testing-strategy.md

3.9 KiB

Стратегия тестирования API


9 обязательных сценариев для каждого endpoint

1. Missing field → 422

async def test_create_missing_field(async_client):
    response = await async_client.post("/api/v1/ideas", json={})
    assert response.status_code == 422

2. Wrong type → 422

async def test_create_wrong_type(async_client):
    response = await async_client.post("/api/v1/ideas", json={
        "title": 123,  # Должна быть строка
        "content": "test",
    })
    assert response.status_code == 422

3. Expired/invalid token → 401

async def test_unauthorized(async_client):
    response = await async_client.get("/api/v1/ideas", headers={
        "Authorization": "Bearer invalid_token"
    })
    assert response.status_code == 401

4. Wrong permissions → 403

async def test_forbidden(async_client, user_token):
    response = await async_client.get(
        "/api/v1/admin/users",
        headers={"Authorization": f"Bearer {user_token}"},
    )
    assert response.status_code == 403

5. Not found → 404

async def test_not_found(async_client, user_token):
    response = await async_client.get(
        "/api/v1/ideas/nonexistent",
        headers={"Authorization": f"Bearer {user_token}"},
    )
    assert response.status_code == 404

6. Conflict → 409

async def test_duplicate_email(async_client):
    # Создать первого пользователя
    await async_client.post("/api/v1/auth/register", json={...})
    # Попробовать создать с тем же email
    response = await async_client.post("/api/v1/auth/register", json={...})
    assert response.status_code == 409

7. Success → 200/201

async def test_create_success(async_client, user_token):
    response = await async_client.post(
        "/api/v1/ideas",
        json={"title": "Test", "content": "Content"},
        headers={"Authorization": f"Bearer {user_token}"},
    )
    assert response.status_code == 201
    data = response.json()
    assert data["title"] == "Test"

8. Rate limit → 429 (если реализован)

async def test_rate_limit(async_client, user_token):
    for _ in range(100):
        await async_client.get("/api/v1/ideas", headers={...})
    response = await async_client.get("/api/v1/ideas", headers={...})
    assert response.status_code == 429

9. Idempotency → тот же результат при повторе

async def test_idempotent_delete(async_client, user_token, idea_id):
    response1 = await async_client.delete(f"/api/v1/ideas/{idea_id}", headers={...})
    response2 = await async_client.delete(f"/api/v1/ideas/{idea_id}", headers={...})
    assert response1.status_code == 204
    assert response2.status_code == 404  # Уже удалено

Структура тестов

tests/
├── conftest.py              # Глобальные фикстуры
├── unit/                    # изолированные тесты
├── integration/
│   ├── conftest.py          # Фикстуры для API тестов
│   ├── test_auth.py         # 9 сценариев для auth
│   ├── test_ideas.py        # 9 сценариев для ideas
│   └── test_admin.py        # 9 сценариев для admin
└── smoke/
    └── test_health.py       # smoke-тесты

[ASK]

  • Все ли 9 сценариев нужны для каждого endpoint? (рекомендация: да, но можно начать с успех + not found + unauthorized)
  • Нужны ли тесты на idempotency? (да, для DELETE и PATCH)
  • Как часто прогонять? (при каждом PR — обязательно, при каждом push — желательно)