Привет, Хабр! Меня зовут Ваня (23 года), и я AQA-инженер. Как и многие из вас, я занимаюсь обучением будущих покорителей Postman и Pytest. И, как многие из вас, я столкнулся с проблемой: все существующие тестовые API — невыносимо скучные.

Todo-листы, интернет-магазины с товарами Item 1, Item 2, API для управления книгами... Серьезно? После пятого GET /todos начинаешь сомневаться не только в своей карьере, но и в смысле бытия. Данные в них стерильны, как операционная, а сценарии предсказуемы, как сюжет российского сериала.

Моим ученикам было скучно. Мне было скучно. Я понял, что нужен свой API. С блэкджеком и... ну, вы поняли. С API, который будет не просто функциональным, но и забавным. Который захочется «потыкать» просто ради того, чтобы увидеть очередной перл в ответе сервера.

Так родился «Cynical Circle API» — API для тех, кто понял жизнь и устал от ванильных примеров.

(Так выглядит наша интерактивная документация. Уже интригует, не правда ли?)

Концепция: тестовый бэкенд для циников

Идея проста: создать API, который имитирует небольшую социальную сеть для... уставших от всего людей. Вместо абстрактных «задач» и «продуктов» у нас есть две сущности:

  1. Пользователи (/users): Бедолаги, решившие зарегистрироваться в системе. О каждом можно сказать, что он «просто занимает место в базе данных».

  2. Посты (/posts): Тленные мысли, крики души и бессмысленные изречения, которые эти пользователи могут публиковать.

Ключевые особенности проекта:

  • Черный юмор: Все описания эндпоинтов, моделей и даже сообщения об ошибках пропитаны здоровой долей цинизма. 404 Not Found? Нет, «Пользователь не найден. Возможно, он обрёл свободу».

  • Zero-dependency (почти): Проект работает «in‑memory». Никаких баз данных, Docker-контейнеров и прочих сложностей. Данные хранятся в обычном Python-списке и сбрасываются при перезапуске. Идеально для быстрых тестов.

  • Современный стек: Написан на FastAPI, что дает нам асинхронность, высокую скорость и, самое главное, автоматическую интерактивную документацию (Swagger UI и ReDoc). Для обучения — это киллер-фича.

  • Прозрачная структура: Код разбит на логические модули (роутеры, модели), что позволяет на его примере объяснять базовые принципы построения бэкенд-приложений.

Заглянем под капот

Давайте быстро пробежимся по архитектуре, чтобы вы понимали, насколько всё просто и прозрачно.

Структура проекта:

cynical_circle_api/
│
├── app/
│   ├── main.py         # Точка входа, здесь живет экземпляр FastAPI
│   ├── routers/        # Логика обработки запросов
│   │   ├── users.py
│   │   └── posts.py
│   └── models/         # Модели данных Pydantic для валидации
│       ├── user.py
│       └── post.py
│
└── requirements.txt      # Зависимости

Модели данных (Pydantic)

Вся валидация входящих и исходящих данных лежит на плечах Pydantic. Модели описаны предельно просто и с саркастичными комментариями.

app/models/user.py

from pydantic import BaseModel, Field
from uuid import UUID

class UserBase(BaseModel):
    username: str = Field(..., description="Имя пользователя, которое он забудет через неделю")
    email: str = Field(..., description="Почта для спама и уведомлений, которые никто не читает")

class User(UserBase):
    id: UUID
    is_active: bool = Field(True, description="Активен ли пользователь или просто занимает место в базе данных")

Роутеры (Endpoints)

Логика каждого эндпоинта — это отдельная функция, обернутая в декоратор FastAPI. Вот, например, как выглядит создание пользователя. Обратите внимание на обработку ошибок.

app/routers/users.py

from fastapi import APIRouter, HTTPException, status
# ... импорты

router = APIRouter()

# Наша "in-memory" база данных
fake_users_db = []

@router.post("/", response_model=User, status_code=status.HTTP_201_CREATED, summary="Создать нового бедолагу")
async def create_user(user_in: UserCreate):
    """
    Регистрирует нового пользователя в системе.
    - **username**: Имя, которое скоро станет частью статистики.
    - **email**: Адрес для получения рекламы казино.
    - **password**: Секретная комбинация, известная вашему провайдеру.
    """
    if any(u.email == user_in.email for u in fake_users_db):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Эта почта уже занята кем-то более удачливым."
        )
    # ... логика создания пользователя

Каждая функция подробно документирована с помощью docstring. FastAPI автоматически подтягивает эти описания в Swagger, делая документацию живой и полезной.

Как запустить этот островок безысходности?

Всё до смешного просто. Понадобится Python 3.8+ и пара минут времени.

1. Создаем и активируем виртуальное окружение:

# Windows
python -m venv venv && venv\Scripts\activate

# macOS/Linux
python3 -m venv venv && source venv/bin/activate

2. Устанавливаем зависимости:

Проекту нужны всего три библиотеки: fastapi, uvicorn для запуска сервера и pydantic.

pip install fastapi uvicorn[standard] pydantic

3. Запускаем сервер:

Сохраните код из [репозитория](ссылка на ваш GitHub репозиторий) и выполните в терминале команду:

uvicorn app.main:app --reload

Сервер поднимется на http://127.0.0.1:8000.

4. Наслаждаемся!

Теперь можно открыть браузер и перейти по одному из адресов:

В Swagger можно не только посмотреть все эндпоинты, но и сразу же отправить на них запросы, посмотреть модели и коды ответов. Для новичков — это идеальная среда для знакомства с REST API.

Пример взаимодействия (для настоящих гуру терминала, ну или просто вставь в Postman)

Конечно, можно использовать и старый добрый cURL.

1. Создадим пользователя:

curl -X 'POST' \
  'http://127.0.0.1:8000/users/' \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "Existential_Coder",
    "email": "coder@example.com",
    "password": "my_eternal_suffering_123"
  }'

В ответ получим JSON с id нашего нового пользователя. Сохраним его.

2. Напишем пост от его имени:

curl -X 'POST' \
  'http://127.0.0.1:8000/posts/?user_id=...ВАШ_ID_ПОЛЬЗОВАТЕЛЯ...' \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Ода рефакторингу",
    "content": "Вчера я 8 часов рефакторил код, который работал. Теперь он не работает, но зато красивый."
  }'

3. Посмотрим на всеобщее уныние:

curl -X 'GET' 'http://127.0.0.1:8000/posts/'

UPD: Добавил документацию для этого API, в котором можно посмотреть всю актуальную реализацию проекта. Это так же будет полезно для наших маленьких любителей подёргать ручки.

Вместо заключения

«Cynical Circle API» — это не просто набор эндпоинтов. Это мой маленький протест против унылых и безжизненных учебных материалов. Это инструмент, который позволяет моим ученикам не просто писать автотесты, но и делать это с улыбкой. Когда твой тест падает, потому что API ответил 404 с сообщением «Удалять нечего. Мир и так пуст», это запоминается лучше, чем безликий {"detail": "Not Found"}.

Проект абсолютно открыт. Берите, пользуйтесь, дорабатывайте. Добавляйте новые эндпоинты (например, комментарии, полные пассивной агрессии), расширяйте модели, меняйте тексты на еще более абсурдные.

Буду рад, если этот проект поможет кому-то сделать процесс обучения автоматизации тестирования чуть менее пресным и чуть более человечным.

Весь код доступен на GitHub.

Спасибо за внимание и всем релизов без багов на проде!

Комментарии (3)


  1. SlavaVSLK
    16.08.2025 10:24

    А чем MSW плох например?


    1. kbones Автор
      16.08.2025 10:24

      Ключевое отличие моей идеи в цели: MSW — это про мокирование (имитацию ответов), а наш API — это про симуляцию живого бэкенда. Студенты учатся работать не с "заглушкой", а с реальным, пусть и простым, сервером, у которого есть состояние. Они могут отправить POST запрос, а затем GET запросом убедиться, что данные сохранились и изменились.

      Плюс, я как преподаватель могу на лету менять логику, добавлять "баги" или новые эндпоинты, подстраиваясь под программу курса, чего с моками не сделаешь так гибко.


      1. SlavaVSLK
        16.08.2025 10:24

        Благодарю за ответ