Python Backend Developer: Полный стек технологий

Комплексный курс подготовки бэкенд-разработчика, охватывающий продвинутый Python, базы данных, асинхронные очереди и инженерные практики. Вы научитесь создавать масштабируемые приложения и работать с современным стеком инструментов.

1. Продвинутый Python, ООП и разработка API на FastAPI

Продвинутый Python, ООП и разработка API на FastAPI

Добро пожаловать в курс Python Backend Developer: Полный стек технологий. Это первая статья нашего цикла, и мы начнем с фундамента, на котором держится современная серверная разработка. Даже если вы уже писали скрипты на Python, создание надежного бэкенда требует более глубокого понимания архитектуры кода и инструментов.

В этой статье мы разберем объектно-ориентированное программирование (ООП) в контексте реальных задач, научимся писать чистый код, соответствующий стандартам индустрии, и создадим свое первое API с помощью фреймворка FastAPI.

Объектно-ориентированное программирование: от теории к практике

Python — это мультипарадигменный язык, но в бэкенд-разработке доминирует ООП. Почему? Потому что бэкенд — это работа с сущностями: пользователями, заказами, товарами. Удобнее всего описывать эти сущности как объекты.

Классы и объекты

Представьте, что вы строите завод по производству автомобилей. У вас есть чертеж, по которому собираются машины. В терминах ООП:

* Класс — это чертеж (шаблон). * Объект (экземпляр) — это конкретный автомобиль, созданный по этому чертежу.

!Визуализация различия между абстрактным классом (чертежом) и конкретными объектами (реализациями).

Рассмотрим пример кода:

Здесь __init__ — это конструктор, который запускается при создании объекта. self — это ссылка на конкретный объект, с которым мы работаем в данный момент.

Три кита ООП

Для успешного прохождения собеседований и написания качественного кода нужно понимать три принципа:

  • Инкапсуляция: Данные и методы работы с ними объединены в классе. Мы можем скрыть внутреннюю реализацию (используя _ или __ перед именем атрибута), предоставляя внешний интерфейс.
  • Наследование: Возможность создавать новые классы на основе существующих. Например, класс AdminUser может наследовать всё от User, но добавить права доступа.
  • Полиморфизм: Способность объектов с разной реализацией отвечать на один и тот же вызов метода. И User, и AdminUser могут иметь метод get_permissions(), но возвращать разные наборы прав.
  • Чистый код и типизация

    В Python 3.5+ появилась поддержка Type Hints (подсказок типов). В современной разработке на FastAPI это не просто рекомендация, а необходимость.

    Почему PEP 8 и типы важны?

    PEP 8 — это свод правил по оформлению кода. Код читается намного чаще, чем пишется. Если ваш код выглядит нестандартно, коллегам будет сложно его поддерживать.

    Сравните два примера:

    Плохой код:

    Хороший код (PEP 8 + Type Hints):

    Использование аннотаций типов (: int, -> int) позволяет IDE (например, PyCharm или VS Code) подсказывать ошибки еще до запуска программы. FastAPI использует эти аннотации для автоматической валидации данных.

    Обработка исключений и работа с файлами

    Бэкенд должен быть устойчивым. Если база данных недоступна или файл отсутствует, сервер не должен падать с ошибкой 500. Он должен корректно обработать ситуацию.

    Конструкция try-except

    Обратите внимание на with open(...). Это менеджер контекста. Он гарантирует, что файл будет закрыт, даже если внутри блока произойдет ошибка. Это критически важно для управления ресурсами сервера.

    Введение в API и FastAPI

    Теперь, когда мы освежили знания Python, перейдем к главной теме — созданию API.

    Что такое API?

    API (Application Programming Interface) — это контракт, по которому одна программа общается с другой. В веб-разработке мы чаще всего используем REST API, где общение происходит по протоколу HTTP.

    !Схематичное изображение того, как клиентское приложение отправляет запрос на сервер через API и получает ответ.

    Почему FastAPI?

    FastAPI — это современный, быстрый (высокопроизводительный) веб-фреймворк для создания API на Python 3.6+.

    Его преимущества: * Скорость: Сопоставим с Node.js и Go (благодаря Starlette и Pydantic). * Асинхронность: Нативная поддержка async / await. * Автоматическая документация: Swagger UI генерируется сам. * Валидация данных: Использует стандартные подсказки типов Python.

    Установка

    Для начала работы нам понадобятся сам фреймворк и сервер ASGI (например, uvicorn):

    Ваше первое приложение

    Создадим файл main.py. В FastAPI мы определяем эндпоинты (точки входа) — это URL-адреса, по которым доступно наше API.

    Разберем код:

  • @app.get("/"): Декоратор, который говорит FastAPI, что функция ниже обрабатывает HTTP GET запросы по адресу /.
  • async def: Мы используем асинхронные функции, что позволяет серверу обрабатывать тысячи запросов одновременно, не блокируясь на ожидании (например, ответа от базы данных).
  • class Item(BaseModel): Это модель Pydantic. Она описывает структуру данных, которую мы ожидаем получить. Если клиент пришлет price как строку "дорого", FastAPI автоматически вернет понятную ошибку валидации.
  • Запуск сервера

    Запустите сервер командой в терминале:

    Теперь откройте браузер и перейдите по адресу http://127.0.0.1:8000/docs. Вы увидите интерактивную документацию Swagger UI, где можно протестировать все ваши эндпоинты прямо из браузера.

    Заключение

    Мы заложили фундамент для курса. Вы узнали, как использовать классы для моделирования данных, почему важна типизация и как за 5 минут поднять быстрый API-сервер.

    В следующей статье мы усложним архитектуру и поговорим о том, как масштабировать приложения, используя очереди сообщений RabbitMQ и Kafka для асинхронной обработки задач.

    2. Профессиональная работа с PostgreSQL: SQL, транзакции и SQLAlchemy

    Профессиональная работа с PostgreSQL: SQL, транзакции и SQLAlchemy

    В предыдущей статье мы создали каркас нашего API на FastAPI. Однако, любое серьезное приложение бесполезно без надежного хранения данных. Переменные в памяти исчезают при перезагрузке сервера, а файлы плохо подходят для одновременного доступа тысяч пользователей. Здесь на сцену выходит PostgreSQL.

    В этой статье мы превратим наше приложение в профессиональный сервис. Мы разберем, как эффективно писать SQL-запросы, что такое ACID-транзакции, и как использовать SQLAlchemy — самый мощный ORM в мире Python — в асинхронном режиме.

    Почему PostgreSQL?

    PostgreSQL (или просто Postgres) — это объектно-реляционная система управления базами данных (СУБД). В мире Python-бэкенда это стандарт де-факто. В отличие от MySQL, Postgres строже относится к стандартам SQL, поддерживает сложные типы данных (JSONB, массивы, геоданные) и обладает невероятной надежностью.

    Реляционная модель данных

    Прежде чем писать код, нужно спроектировать структуру. Реляционные базы данных хранят информацию в таблицах, связанных между собой ключами.

    Представим наш интернет-магазин. У нас есть:

  • Users (Пользователи)
  • Orders (Заказы)
  • Один пользователь может сделать много заказов. Это связь один-ко-многим (One-to-Many).

    !Схема связи таблиц пользователей и заказов.

    SQL: За пределами SELECT *

    ORM (Object-Relational Mapping) облегчает жизнь, но без знания SQL вы не сможете оптимизировать сложные запросы. Рассмотрим ключевые концепции.

    Объединение таблиц (JOIN)

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

    Существует несколько типов JOIN:

    INNER JOIN (по умолчанию): Возвращает строки, только если совпадение найдено в обеих* таблицах. Если у пользователя нет заказов, он не попадет в выборку. LEFT JOIN: Возвращает все* строки из левой таблицы (users) и совпавшие из правой (orders). Если заказов нет, поля заказа будут NULL.

    Агрегация и группировка

    Бизнесу часто нужны отчеты. Например: "Сколько денег потратил каждый пользователь?". Для этого используются агрегатные функции (SUM, COUNT, AVG) и группировка GROUP BY.

    Здесь мы:

  • Объединили таблицы.
  • Сгруппировали строки по имени пользователя.
  • Посчитали сумму заказов для каждой группы.
  • Отфильтровали (HAVING) только тех, кто потратил больше 1000.
  • Транзакции и ACID

    Транзакция — это последовательность операций с базой данных, которая выполняется как единое целое. Это критически важно для финансовой целостности.

    Представьте перевод денег от Алисы к Бобу:

  • Списать 100 рублей у Алисы.
  • Зачислить 100 рублей Бобу.
  • Если шаг 1 выполнится, а на шаге 2 сервер упадет (выключат свет), деньги исчезнут. Транзакции предотвращают это.

    Принципы ACID

    Надежность транзакций описывается аббревиатурой ACID:

  • Atomicity (Атомарность): Транзакция выполняется целиком или не выполняется вовсе. Все или ничего.
  • Consistency (Согласованность): Транзакция переводит базу из одного корректного состояния в другое (соблюдаются все ограничения, например, баланс не может быть отрицательным).
  • Isolation (Изолированность): Параллельные транзакции не должны мешать друг другу.
  • Durability (Долговечность): Если транзакция подтверждена (committed), данные сохранены навсегда, даже в случае сбоя питания.
  • !Визуализация работы Commit и Rollback в транзакциях.

    В SQL управление транзакциями выглядит так:

    SQLAlchemy 2.0: Асинхронная работа с БД

    В современном Python мы используем SQLAlchemy вместе с драйвером asyncpg. Это позволяет нашему FastAPI приложению не блокироваться во время ожидания ответа от базы данных.

    Установка зависимостей

    Настройка подключения (Engine и Session)

    Создадим файл database.py. Нам нужно создать "движок" и фабрику сессий.

    Описание моделей

    Модели — это классы Python, которые отображаются на таблицы БД. Создадим models.py.

    Выполнение запросов

    В SQLAlchemy 2.0 используется новый синтаксис запросов, похожий на чистый SQL. Интегрируем это в FastAPI.

    Обратите внимание на await db.commit(). SQLAlchemy автоматически начинает транзакцию при первом действии. Метод commit() сохраняет изменения, соблюдая принципы ACID.

    Миграции с Alembic

    В процессе разработки структура БД меняется. Удалять и создавать таблицы заново нельзя — потеряются данные. Для этого используют миграции.

    Alembic — это инструмент для управления версиями схемы БД. Он создает скрипты (ревизии), которые описывают изменения (например, "добавить колонку age в таблицу users").

    Базовый процесс работы:

  • alembic init alembic — инициализация проекта.
  • Настройка alembic.ini и env.py (указание URL базы и метаданных моделей).
  • alembic revision --autogenerate -m "Initial migration" — создание файла миграции на основе сравнения моделей и реальной БД.
  • alembic upgrade head — применение изменений к базе.
  • Заключение

    Сегодня мы сделали огромный шаг вперед. Мы перешли от хранения данных в памяти к профессиональной СУБД PostgreSQL. Мы изучили, как писать SQL-запросы, поняли важность транзакций для целостности данных и настроили асинхронную работу через SQLAlchemy.

    В следующей статье мы разберем, как обрабатывать тяжелые задачи в фоне, не заставляя пользователя ждать, используя брокеры сообщений RabbitMQ и Kafka.

    3. Асинхронная архитектура и брокеры сообщений RabbitMQ и Kafka

    Асинхронная архитектура и брокеры сообщений RabbitMQ и Kafka

    В предыдущих статьях мы построили надежный API на FastAPI и подключили к нему базу данных PostgreSQL. Наше приложение умеет быстро сохранять и отдавать данные. Но что произойдет, если пользователю нужно выполнить действие, занимающее много времени? Например, сгенерировать тяжелый отчет, обработать видео или отправить письмо с подтверждением регистрации?

    Если мы будем делать это внутри обычного HTTP-запроса, пользователь будет смотреть на крутящийся индикатор загрузки 10, 20 или 30 секунд. В современном вебе это недопустимо. Сервер должен ответить мгновенно: «Я принял задачу, результат будет позже».

    Именно здесь на сцену выходят брокеры сообщений и асинхронная архитектура.

    Проблема синхронного взаимодействия

    Представьте ресторан. В синхронной модели (без очередей) официант принимает заказ, идет на кухню и стоит над душой у повара, пока тот жарит стейк. Только когда стейк готов, официант несет его клиенту и идет к следующему столику. Это неэффективно.

    В асинхронной модели официант берет заказ, вешает чек (сообщение) на специальную ленту (очередь) и тут же возвращается в зал к другим гостям. Повара на кухне (воркеры) берут чеки по мере освобождения и готовят еду.

    !Сравнение блокирующей синхронной обработки и асинхронной обработки через очередь сообщений.

    Основные понятия

    Чтобы организовать такую работу в коде, нам нужны три элемента:

  • Producer (Продюсер) — тот, кто создает задачу (наше FastAPI приложение).
  • Broker (Брокер) — посредник, который хранит сообщения (RabbitMQ, Kafka).
  • Consumer (Консьюмер/Воркер) — отдельный процесс, который забирает сообщения из брокера и выполняет тяжелую работу.
  • RabbitMQ: Умный почтальон

    RabbitMQ — это самый популярный традиционный брокер сообщений. Он реализует протокол AMQP (Advanced Message Queuing Protocol). Его главная задача — гарантировать доставку сообщения нужному получателю.

    Как это работает?

    В RabbitMQ продюсер никогда не отправляет сообщения напрямую в очередь. Он отправляет их в Exchange (обменник). Обменник решает, в какую очередь положить письмо, основываясь на правилах маршрутизации.

    Типы обменников:

    * Direct: Сообщение идет в очередь с точным совпадением ключа маршрутизации (routing key). Например, ключ error попадет только в очередь ошибок. Fanout: Сообщение копируется во все* привязанные очереди. Полезно для рассылок. Topic: Маршрутизация по шаблону. Например, logs. соберет и logs.error, и logs.info.

    Пример на Python (библиотека pika)

    Допустим, мы хотим отправить приветственное письмо пользователю.

    Продюсер (FastAPI):

    Консьюмер (отдельный скрипт):

    В RabbitMQ сообщение обычно удаляется из очереди сразу после того, как консьюмер подтвердил его получение (ack). Это делает его идеальным для задач типа «сделать и забыть».

    Apache Kafka: Распределенный журнал

    Kafka работает иначе. Это не просто очередь, это стриминговая платформа. Представьте Kafka как огромный журнал событий (лог), в который можно только дописывать данные в конец.

    Ключевые отличия Kafka от RabbitMQ

  • Хранение данных: В RabbitMQ сообщения исчезают после обработки. В Kafka сообщения хранятся на диске определенное время (например, 7 дней), даже если их уже прочитали.
  • Производительность: Kafka создана для обработки миллионов сообщений в секунду (Big Data, аналитика, логи).
  • Потребление: В Kafka консьюмер сам помнит, на каком месте (offset) он остановился.
  • Структура Kafka

    * Topic (Топик): Категория сообщений (аналог таблицы в БД или папки с файлами). * Partition (Партиция): Топик делится на части для параллельной обработки. * Offset (Смещение): Уникальный номер сообщения в партиции.

    !Визуализация топика Kafka, разделенного на партиции с указанием смещения (offset).

    Что выбрать?

    Этот вопрос часто задают на собеседованиях.

    | Характеристика | RabbitMQ | Apache Kafka | | :--- | :--- | :--- | | Модель | Умный брокер, глупый потребитель | Глупый брокер, умный потребитель | | Сценарий | Фоновые задачи, сложная маршрутизация | Стриминг данных, логирование, аналитика | | Хранение | В памяти (преимущественно) | На диске (надежно) | | Скорость | Высокая | Экстремально высокая |

    Выбирайте RabbitMQ, если вам нужно просто вынести долгие задачи (отправка почты, ресайз картинок) из основного потока веб-приложения. Это стандартный выбор для бэкенда на Python (часто в связке с библиотекой Celery).

    Выбирайте Kafka, если вы строите систему микросервисов, где одно событие (например, «Пользователь купил товар») должно быть обработано пятью разными сервисами (Склад, Доставка, Аналитика, Бонусы), и вам важна история событий.

    Интеграция в архитектуру

    Вернемся к нашему курсу. Как это меняет наше приложение?

    Раньше:

  • POST /register
  • Сохранить в БД.
  • Отправить email (ждем 2 сек).
  • Вернуть ответ 200 OK.
  • Теперь:

  • POST /register
  • Сохранить в БД.
  • Скинуть сообщение в RabbitMQ: {"task": "send_email", "email": "..."}.
  • Вернуть ответ 200 OK (мгновенно).
  • Где-то на фоне работает воркер, который разгребает очередь. Если почтовый сервис упал, воркер просто попробует выполнить задачу позже, а пользователь даже не заметит сбоя.

    Заключение

    Внедрение брокеров сообщений делает систему слабосвязанной (decoupled) и масштабируемой. Если задач станет слишком много, мы просто запустим еще 10 воркеров-консьюмеров, не трогая основной код API.

    В следующей статье мы поговорим о том, как автоматизировать проверку качества нашего кода и доставку его на сервер, изучив CI/CD и написание тестов.

    4. Инженерная культура: Git, Code Review и процессы CI/CD

    Инженерная культура: Git, Code Review и процессы CI/CD

    Мы прошли большой путь. Мы научились писать быстрые API на FastAPI, проектировать базы данных в PostgreSQL и масштабировать нагрузку с помощью RabbitMQ. Но есть один нюанс: пока что весь этот код живет только на вашем локальном компьютере.

    В реальной разработке над проектом работают команды из десятков человек. Если каждый будет менять код как ему вздумается, проект превратится в хаос за пару дней. Чтобы этого избежать, существует инженерная культура.

    В этой статье мы разберем три столпа профессиональной разработки:

  • Git Flow — как управлять версиями кода и не потерять изменения.
  • Code Review — как проверять код коллег и повышать качество.
  • CI/CD — как автоматизировать тестирование и доставку кода на сервер.
  • Git: От «сохранить» к управлению версиями

    Вы наверняка уже знакомы с базовыми командами git add, git commit и git push. Но в командной работе этого недостаточно. Главное правило профессиональной разработки: никто не пишет код (commit) напрямую в ветку main (или master).

    Ветвление (Branching)

    Представьте, что ветка main — это чистовик. Это та версия кода, которая прямо сейчас работает на сервере и обслуживает пользователей. Любая ошибка здесь стоит денег.

    Для каждой новой задачи (фичи) или исправления бага создается отдельная ветка. Этот подход называется Feature Branch Workflow.

    !Визуализация процесса создания ветки для новой задачи и её слияния с основной веткой.

    Алгоритм работы разработчика:

  • Получить актуальный код:
  • Создать новую ветку:
  • Название ветки должно быть говорящим. Обычно используют префиксы feature/, bugfix/ или hotfix/.
  • Написать код и закоммитить:
  • Отправить ветку в удаленный репозиторий (GitHub/GitLab):
  • Pull Request (Merge Request)

    Когда код написан и отправлен в репозиторий, он не попадает в main автоматически. Вы создаете Pull Request (PR) — запрос на слияние.

    PR — это не просто кнопка «Merge». Это место для дискуссии. Здесь ваши коллеги видят разницу (diff) между вашей веткой и основной. Именно здесь происходит магия улучшения кода.

    Code Review: Больше, чем поиск ошибок

    Code Review (ревью кода) — это процесс, когда другие разработчики смотрят ваш код перед тем, как он попадет в основную ветку. Это самый мощный инструмент обучения в команде.

    Зачем это нужно?

  • Поиск багов: Автор кода часто имеет «замыленный» взгляд и не видит очевидных ошибок.
  • Обмен знаниями: Junior учится, читая комментарии Senior-разработчика. Senior узнает о новых фишках языка, которые нашел Junior.
  • Единый стиль: Ревью помогает поддерживать код в таком состоянии, будто его писал один человек.
  • Что проверять на ревью?

    Если вы проверяете чужой код, обратите внимание на:

    * Логика: Решает ли код поставленную задачу? Нет ли граничных случаев (например, деление на ноль), которые не обработаны? * Читаемость: Понятны ли названия переменных? Разбит ли код на функции? Соблюдается ли PEP 8? * Безопасность: Нет ли SQL-инъекций (хотя SQLAlchemy нас защищает) или захардкоженных паролей? * Тесты: Написал ли автор тесты для своего нового функционала?

    > Code Review — это критика кода, а не автора. Будьте вежливы и конструктивны.

    Автоматизация: CI/CD

    Человеческий фактор — главная причина сбоев. Разработчик может забыть запустить тесты или не проверить стиль кода. Чтобы исключить это, мы используем роботов.

    CI/CD — это набор практик и инструментов, которые автоматизируют интеграцию и доставку кода.

    !Схематичное изображение автоматизированного конвейера доставки кода.

    CI (Continuous Integration) — Непрерывная интеграция

    CI отвечает за то, чтобы код разных разработчиков корректно собирался вместе. Как только вы создаете Pull Request, сервер CI (например, GitHub Actions, GitLab CI, Jenkins) автоматически запускает проверки.

    Типичный пайплайн (сценарий) CI для Python-проекта:

  • Linter (Линтер): Проверка стиля кода. Инструменты flake8, black или ruff проверяют, что вы не забыли пробелы, не оставили лишних импортов и соблюдаете стандарты.
  • Type Checker: Инструмент mypy проверяет, что типы данных совпадают с аннотациями (Type Hints), которые мы изучили в первой статье.
  • Tests: Запуск модульных тестов через pytest.
  • Если хоть один этап падает (красный крестик), слияние ветки блокируется. Нельзя влить сломанный код в main.

    CD (Continuous Delivery / Deployment) — Непрерывная доставка

    Если все проверки CI прошли успешно (зеленые галочки), вступает в дело CD. Это автоматическая выкладка кода на сервер.

    * Continuous Delivery: Код готов к релизу, но кнопку «Deploy» нажимает человек. * Continuous Deployment: Код улетает в продакшн полностью автоматически сразу после слияния в main.

    Практика: Настройка GitHub Actions

    Давайте настроим простой CI для нашего FastAPI проекта. В корне репозитория создадим файл .github/workflows/ci.yml.

    GitHub Actions использует формат YAML. Это декларативное описание того, что нужно сделать.

    Разберем ключевые моменты:

    * on: Триггеры. Мы говорим: «Запускайся, когда кто-то пушит в main или открывает PR». * runs-on: ubuntu-latest: GitHub выделяет нам временный сервер на Linux. * steps: Последовательность команд. Это то же самое, что вы бы вводили в терминале, только делает это робот.

    Теперь, если вы попытаетесь запушить код с синтаксической ошибкой, GitHub Actions покажет ошибку, и вы увидите это прямо в интерфейсе Pull Request.

    Тестирование: Pytest

    CI бесполезен без тестов. В Python стандартом является библиотека pytest. Она позволяет писать тесты лаконично.

    Создадим файл test_main.py:

    Команда assert — это сердце теста. Она проверяет истинность утверждения. Если response.status_code не равен 200, тест упадет, CI покраснеет, и плохой код не попадет в продакшн.

    Пирамида тестирования

    Не все тесты одинаковы. Существует концепция пирамиды тестирования:

  • Unit-тесты (Модульные): Самые быстрые и дешевые. Проверяют одну функцию в изоляции. Их должно быть больше всего.
  • Integration-тесты (Интеграционные): Проверяют, как компоненты работают вместе (например, API + База данных).
  • E2E-тесты (End-to-End): Имитируют действия реального пользователя (открыть браузер, нажать кнопку). Самые медленные.
  • В нашем курсе мы фокусируемся на Unit и Integration тестах, так как они критичны для бэкенда.

    Заключение

    Инженерная культура — это то, что отличает любителя от профессионала. Использование Git Flow защищает кодовую базу от хаоса. Code Review повышает качество кода и компетенции команды. CI/CD позволяет спать спокойно, зная, что роботы проверили каждую строчку перед тем, как она попала к пользователям.

    Теперь у нас есть полный цикл: от написания кода на Python до его автоматической проверки. Но как запустить наше приложение на любом сервере, не беспокоясь о версиях библиотек и настройках ОС? В следующей статье мы поговорим о Docker и контейнеризации.

    5. Обеспечение качества: тестирование с pytest и системное мышление

    Обеспечение качества: тестирование с pytest и системное мышление

    В предыдущей статье мы настроили CI/CD пайплайн, который автоматически запускает проверки кода при каждом изменении. Но автоматизация бесполезна, если проверять нечего. Пустой пайплайн всегда будет «зеленым», но это не гарантирует работоспособность приложения.

    Сегодня мы займемся наполнением этого пайплайна. Мы перейдем от простого написания кода к инженерному обеспечению качества (Quality Assurance). Мы разберем библиотеку pytest, научимся писать модульные и интеграционные тесты для FastAPI, а также освоим мокирование (mocking) для изоляции внешних зависимостей, таких как PostgreSQL и RabbitMQ.

    Системное мышление в тестировании

    Прежде чем писать assert, нужно понять философию. Тестирование — это не просто поиск багов. Это способ доказать, что система ведет себя предсказуемо.

    Любая сложная система состоит из множества компонентов. Надежность всей системы зависит от надежности каждой ее части. Математически это можно выразить следующей формулой надежности для последовательной системы:

    Где: * — общая надежность системы (вероятность безотказной работы). * — надежность -го компонента. * — символ произведения (перемножение всех элементов). * — количество компонентов.

    Если у вас есть 3 компонента (API, База данных, Брокер очередей), и надежность каждого составляет 99% (), то общая надежность системы будет:

    То есть 97%. Чем больше компонентов, тем ниже общая надежность, если мы не обеспечиваем качество каждого узла. Именно поэтому мы пишем тесты: чтобы гарантировать, что стремится к 1.

    Пирамида тестирования

    Чтобы тестирование было эффективным, оно должно быть структурированным. В индустрии принята концепция Пирамиды тестирования.

    !Визуализация пирамиды тестирования, показывающая соотношение разных типов тестов.

  • Unit-тесты (Модульные): Проверяют изолированную функцию или класс. Никаких баз данных или сети. Работают миллисекунды.
  • Integration-тесты (Интеграционные): Проверяют связку компонентов (например, API + БД).
  • E2E-тесты (End-to-End): Проверяют сценарий целиком, как пользователь.
  • В этом курсе мы сосредоточимся на первых двух уровнях.

    Знакомство с pytest

    pytest — это стандарт де-факто в мире Python. Он прост для новичков, но обладает мощнейшим функционалом для профи. В отличие от встроенного unittest, pytest не требует создания классов-наследников и использует обычный оператор assert.

    Установка:

    Ваш первый тест

    Создадим файл test_simple.py:

    Запуск тестов выполняется командой pytest в терминале. Библиотека автоматически найдет все файлы, начинающиеся на test_, и функции внутри них, начинающиеся на test_.

    Фикстуры (Fixtures): Управление контекстом

    В реальных приложениях тестам часто нужны предварительные данные: подключение к базе, созданный пользователь или временный файл. В pytest для этого используются фикстуры.

    Фикстура — это функция, помеченная декоратором @pytest.fixture. Она подготавливает данные перед тестом и (опционально) очищает их после.

    Файл conftest.py

    Если фикстура нужна в нескольких файлах тестов, ее не нужно копировать. Достаточно создать файл conftest.py в корне папки с тестами. Pytest автоматически подгрузит фикстуры оттуда и сделает их доступными глобально.

    Тестирование FastAPI приложений

    FastAPI предоставляет класс TestClient (на базе Starlette/Httpx), который позволяет делать запросы к вашему API без запуска реального сервера. Это очень быстро.

    Предположим, у нас есть main.py:

    Напишем тест в test_api.py:

    Изоляция и Mocking: Работа с зависимостями

    Это самая сложная, но важная часть. Наш бэкенд работает с PostgreSQL и RabbitMQ. В Unit-тестах мы не должны зависеть от наличия реальной базы данных. Если база упала, юнит-тест функции валидации данных не должен падать.

    Для подмены реальных объектов «пустышками» используется техника Mocking (от англ. mock — имитация).

    Пример с RabbitMQ

    Допустим, у нас есть функция, отправляющая сообщение:

    Мы не хотим поднимать RabbitMQ для теста. Мы используем unittest.mock.

    Мы проверили логику нашего кода, не отправив ни одного байта в сеть. Это и есть суть модульного тестирования.

    Параметризация тестов

    Принцип DRY (Don't Repeat Yourself) работает и в тестах. Если вам нужно проверить функцию сложения на 10 разных парах чисел, не пишите 10 функций. Используйте @pytest.mark.parametrize.

    Pytest сгенерирует 5 отдельных тестов из этой одной функции. Если один упадет, остальные продолжат выполняться.

    Покрытие кода (Code Coverage)

    Как понять, достаточно ли тестов? Для этого существует метрика Code Coverage — процент кода, который был выполнен во время тестов.

    Установим плагин:

    Запустим тесты с отчетом:

    Вы увидите таблицу, показывающую, какие файлы протестированы на 100%, а какие — на 20%. Однако, помните: 100% покрытие не гарантирует отсутствие багов. Оно лишь гарантирует, что каждая строчка кода была запущена хотя бы раз. Логические ошибки могут остаться.

    Интеграционное тестирование с базой данных

    В отличие от Unit-тестов, интеграционные тесты должны работать с реальной (или тестовой) базой данных. Для этого в conftest.py создают фикстуру, которая:

  • Создает чистую БД (или таблицы).
  • Прогоняет тест.
  • Откатывает транзакцию или очищает таблицы.
  • Это гарантирует, что тесты не влияют друг на друга (изоляция данных).

    Заключение

    Качество кода — это не случайность, а результат системного подхода. Используя pytest, фикстуры и моки, вы создаете защитную сетку для своего проекта. Теперь вы можете рефакторить код, обновлять библиотеки и добавлять фичи, не боясь сломать то, что уже работает.

    В следующей статье мы решим проблему «на моем компьютере все работает», упаковав наше приложение и все его зависимости в контейнеры с помощью Docker.