Привет, Хабр! Меня зовут Ваня (23 года), и я AQA-инженер. Как и многие из вас, я занимаюсь обучением будущих покорителей Postman и Pytest. И, как многие из вас, я столкнулся с проблемой: все существующие тестовые API — невыносимо скучные.
Todo-листы, интернет-магазины с товарами Item 1
, Item 2
, API для управления книгами... Серьезно? После пятого GET /todos
начинаешь сомневаться не только в своей карьере, но и в смысле бытия. Данные в них стерильны, как операционная, а сценарии предсказуемы, как сюжет российского сериала.
Моим ученикам было скучно. Мне было скучно. Я понял, что нужен свой API. С блэкджеком и... ну, вы поняли. С API, который будет не просто функциональным, но и забавным. Который захочется «потыкать» просто ради того, чтобы увидеть очередной перл в ответе сервера.
Так родился «Cynical Circle API» — API для тех, кто понял жизнь и устал от ванильных примеров.
(Так выглядит наша интерактивная документация. Уже интригует, не правда ли?)
Концепция: тестовый бэкенд для циников
Идея проста: создать API, который имитирует небольшую социальную сеть для... уставших от всего людей. Вместо абстрактных «задач» и «продуктов» у нас есть две сущности:
Пользователи (
/users
): Бедолаги, решившие зарегистрироваться в системе. О каждом можно сказать, что он «просто занимает место в базе данных».Посты (
/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. Наслаждаемся!
Теперь можно открыть браузер и перейти по одному из адресов:
http://127.0.0.1:8000/docs
— интерактивная документация Swagger UI.http://127.0.0.1:8000/redoc
— альтернативный вариант документации ReDoc.
В 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.
Спасибо за внимание и всем релизов без багов на проде!
SlavaVSLK
А чем MSW плох например?
kbones Автор
Ключевое отличие моей идеи в цели: MSW — это про мокирование (имитацию ответов), а наш API — это про симуляцию живого бэкенда. Студенты учатся работать не с "заглушкой", а с реальным, пусть и простым, сервером, у которого есть состояние. Они могут отправить POST запрос, а затем GET запросом убедиться, что данные сохранились и изменились.
Плюс, я как преподаватель могу на лету менять логику, добавлять "баги" или новые эндпоинты, подстраиваясь под программу курса, чего с моками не сделаешь так гибко.
SlavaVSLK
Благодарю за ответ