Когда тесты запускаются только на ноутбуке разработчика, они перестают делать свою настоящую работу: обнаруживать регрессии до того, как код попадёт в продакшн. Непрерывная интеграция (CI) -- это практика автоматического запуска полного тест-сьюта при каждом push или pull request на чистой машине, ничего не знающей о вашей локальной настройке. Два инструмента, с которыми вы будете встречаться чаще всего -- **GitHub Actions** (встроен в GitHub, бесплатен для публичных репозиториев) и **tox** (Python-специфичный инструмент автоматизации тестов).
## Почему CI -- это не просто "pytest на сервере"
Запуск pytest в CI вводит ограничения, которых нет локально:
- **Никаких предустановленных пакетов.** CI-раннер стартует с базового образа ОС. Ваш воркфлоу должен устанавливать каждую зависимость.
- **Никаких `.env`-файлов.** Секреты и переменные окружения должны предоставляться через хранилище секретов CI-системы.
- **Ненулевой код выхода важен.** pytest завершается с кодом `1` когда любой тест падает. CI-платформы воспринимают ненулевой выход как сбой пайплайна -- pull request не может быть слит.
- **Воспроизводимость.** Тест, проходящий локально но падающий в CI, почти всегда означает что тест зависит от чего-то в вашем окружении (глобально установленный пакет, локальный файл, запущенный сервис).
## Основы GitHub Actions
GitHub Actions запускает воркфлоу, определённые как YAML-файлы в `.github/workflows/`. Воркфлоу срабатывает на события (push, pull request, расписание) и выполняет последовательность шагов внутри раннера -- временной виртуальной машины.
Минимальная структура воркфлоу для pytest:
```yaml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
run: pytest
```
Каждая строка `uses:` подключает заранее подготовленное действие. `actions/checkout@v4` клонирует репозиторий в раннер. `actions/setup-python@v5` устанавливает запрошенную версию Python и добавляет её в `PATH`.
## Кэширование загрузок pip
Установка пакетов с нуля при каждом запуске медленная. GitHub Actions позволяет кэшировать кэш загрузок pip между запусками:
```yaml
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
```
`key` включает хеш `requirements.txt`, поэтому кэш сбрасывается при изменении зависимостей. `restore-keys` обеспечивает запасной вариант, соответствующий ОС даже при промахе точного ключа -- он восстанавливает частичный кэш, который всё равно быстрее чем начинать с нуля.
## Провал пайплайна при падении покрытия
Покрытие измеряет, какой процент кода выполняется тестами. Добавление `--cov` к pytest (с установленным `pytest-cov`) создаёт отчёт покрытия. Добавление `--cov-fail-under=80` заставляет pytest завершаться с кодом `2` при падении покрытия ниже 80%, что проваливает CI-пайплайн:
```yaml
- name: Run tests with coverage
run: pytest --cov=src --cov-fail-under=80 --cov-report=term-missing --cov-report=xml
```
`--cov-report=xml` записывает `coverage.xml` в формате Cobertura -- многие CI-интеграции и инструменты проверки PR могут парсить этот файл для отображения дифф покрытия. Если XML-файл не нужен, флаг можно опустить.
`--cov=src` указывает pytest-cov какой каталог измерять (исходный код, а не сами тесты). `--cov-report=term-missing` печатает таблицу, показывающую непокрытые строки.
Порог -- это политическое решение. 80% -- распространённая отправная точка; 100% часто нецелесообразны и контрпродуктивны (вы в итоге пишете тесты, которые не тестируют ничего значимого, лишь бы достичь числа).
## Тестирование на нескольких версиях Python с tox
`tox` -- инструмент, создающий изолированные виртуальные окружения и запускающий тест-сьют в каждом. Наиболее полезен авторам библиотек, которым нужно проверять совместимость с несколькими версиями Python, но также распространён в прикладных проектах, которым нужна поддержка разных сред.
Минимальный `tox.ini`:
```ini
[tox]
envlist = py311, py312
[testenv]
deps = -r requirements.txt
commands = pytest {posargs}
```
Запуск `tox` локально создаёт виртуальные окружения `.tox/py311/` и `.tox/py312/`, устанавливает зависимости в каждое и запускает `pytest` внутри каждого. `{posargs}` передаёт любые аргументы, которые вы добавляете после `tox --`, напрямую в pytest (`tox -- -k test_login`).
В GitHub Actions можно использовать **матрицу** для запуска tox на нескольких версиях:
```yaml
strategy:
matrix:
python-version: ["3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install tox
- run: tox -e py${{ matrix.python-version | replace('.', '') }}
```
Это запускает два параллельных задания -- по одному на каждую версию -- и помечает воркфлоу как провалившийся если любое из них падает.
## Настройка pytest для CI
Вы можете хранить CI-специфичные дефолты pytest в `pytest.ini` (или `pyproject.toml`), чтобы каждый запуск -- локальный и CI -- использовал одинаковые настройки:
```ini
[pytest]
addopts = -v --tb=short
testpaths = tests
```
`--tb=short` даёт достаточно трассировки для диагностики падений без заполнения лога шумом. `-v` (подробный) печатает имя каждого теста при запуске, что помогает при чтении CI-логов видеть какой тест упал.
## Отчёты о результатах тестов
Некоторые CI-системы и интеграции GitHub могут отображать результаты тестов как структурированный отчёт, а не сырой вывод терминала. pytest может создавать JUnit XML-файл:
```
pytest --junitxml=reports/test-results.xml
```
GitHub Actions может затем загрузить его как артефакт:
```yaml
- name: Upload test results
uses: actions/upload-artifact@v4
with:
name: test-results
path: reports/test-results.xml
```
Это не меняет прохождение или провал пайплайна -- просто делает результаты доступными для скачивания после запуска, что полезно при отладке непостоянных сбоев.
Полный CI-воркфлоу: GitHub Actions + tox + coverage
Создайте файл `.github/workflows/tests.yml` для проекта со следующей структурой:
```
my_project/
+-- src/
| +-- app.py
+-- tests/
| +-- test_app.py
+-- requirements-dev.txt # содержит: pytest
```
Требования к воркфлоу:
1. Срабатывать на `push` и `pull_request` в ветку `main`
2. Запускаться на `ubuntu-latest` с Python 3.12
3. Проверять код
4. Устанавливать зависимости из `requirements-dev.txt`
5. Запускать `pytest`
Напишите полное содержимое YAML-файла воркфлоу.
# Напишите содержимое .github/workflows/tests.yml ниже.
# Это YAML-файл, а не Python -- заполните каждую секцию.
# name: ...
# on:
# ...
# jobs:
# test:
# runs-on: ...
# steps:
# - ...
Напишите `tox.ini` для проекта, где:
- Тесты должны запускаться на Python 3.11 и 3.12
- Зависимости находятся в `requirements.txt` и `requirements-dev.txt`
- Команда тестирования -- `pytest` с любыми аргументами, которые передаёт пользователь
Также напишите команду для:
1. Запуска tox для всех окружений
2. Запуска tox только для Python 3.12
3. Передачи `-v` в pytest через tox
# tox.ini
[tox]
envlist = ...
[testenv]
deps =
...
commands =
...
# Команды (напишите в комментариях):
# 1. Запустить все окружения:
# tox ...
#
# 2. Запустить только Python 3.12:
# tox ...
#
# 3. Передать -v в pytest:
# tox ...
У вас есть следующий шаг воркфлоу, запускающий тесты с покрытием:
```yaml
- name: Run tests with coverage
run: pytest --cov=src --cov-report=term-missing --cov-fail-under=80 --cov-report=xml
```
Добавьте шаг после него, который:
1. Загружает `coverage.xml` как GitHub Actions артефакт с именем `coverage-report`
2. Выполняется даже если предыдущий шаг упал (например, когда порог покрытия не выполнен)
Напишите только новый шаг в YAML.
# Напишите шаг для добавления после 'Run tests with coverage':
# - name: Upload coverage report
# uses: ...
# with:
# ...
# if: ...
Команда имеет пустой `pytest.ini` и CI-воркфлоу где шаг тестирования:
```yaml
- name: Run tests
run: pytest -v --tb=short --strict-markers tests/
```
Проблема: разработчики, запускающие `pytest` локально, получают другой вывод чем CI (без `-v`, полные трассировки), и путь `tests/` приходится повторять как в файле воркфлоу, так и в любых локальных скриптах.
Исправьте это, написав `pytest.ini` который:
1. Устанавливает `addopts` так чтобы `-v --tb=short --strict-markers` всегда применялись
2. Устанавливает `testpaths` так чтобы pytest всегда искал в `tests/` по умолчанию
3. Регистрирует три кастомные марки, чтобы избежать `PytestUnknownMarkWarning`: `unit`, `integration`, `slow`
После этого изменения шаг CI должен быть просто `pytest` без дополнительных аргументов.
Напишите полный `pytest.ini` и упрощённый CI-шаг.
# pytest.ini
[pytest]
addopts = -v --tb=short --strict-markers
testpaths = tests
markers =
unit: fast unit tests with no I/O
integration: tests that hit the database or network
slow: tests that take more than 1 second
# Упрощённый CI-шаг:
# - name: Run tests
# run: pytest
No split tab
Настройки cookies
Мы используем необходимые cookies для работы сайта. С вашего разрешения мы также можем сохранять настройки сайта и использовать аналитические и рекламные cookies, чтобы понимать использование сайта и поддерживать развитие проекта.
* Вы всегда можете изменить свой выбор в настройках сайта.
Выберите категории cookies
Настройки аналитики
Можно отключить аналитику использования платформы. Также можно отправить в Google Analytics запрос на удаление данных об использовании этого сайта, связанных с этим браузером.
Учебный workspace
Учитесь, читая, запуская код и решая задачи.
Практикуйте программирование с пояснениями тем, упражнениями, инструментами browser IDE, проверкой regex и тренировкой печати кода в одном workspace.
Открывайте инструменты во вкладках.Упражнения, IDE-инструменты и тренажеры остаются доступными как вкладки сайта.
Переключайтесь без потери контекста.Переходите между пояснениями, кодом и утилитами, сохраняя свое место.
Используйте sidebar как карту.Левые панели содержат навигацию, настройки, файлы, libraries и управление инструментами.
PythonJavaScriptSQLite
Одна IDE, три практичных режима
Python в браузере.Запускайте небольшие скрипты, пробуйте библиотеки и тренируйте API-запросы без установки.
JavaScript для быстрых экспериментов.Проверяйте код для браузера и сравнивайте идеи рядом с учебными материалами.
SQLite для практики с данными.Открывайте обозреватель базы данных, изучайте таблицы, пишите запросы и учитесь SQL локально.
ТемаIDE
Работайте рядом в split tabs
Держите инструкции перед глазами.Откройте упражнение или справочную страницу рядом с IDE, вместо постоянных переключений.
Сравнивайте инструменты во время обучения.Размещайте проверки regex, пояснения и эксперименты с кодом рядом, когда это нужно для задачи.
Закройте split, когда закончите.Workspace вернётся к одной сфокусированной вкладке, а открытые вкладки сайта останутся доступны.
Тренажер слепой печати кода
Или просто текста
Тренажер рассчитан на физическую клавиатуру.Откройте этот раздел на ноутбуке или компьютере с широким экраном. На телефоне тренировка слепой печати не будет корректной.
Скорость: 0 зн/мин
0 слов/мин
Лучшая скорость (60с): 0 зн/мин
0 слов/мин
Ошибки: 0
Общее время: 0.0 с
Для активации режима слепого набора не подсматривайте на физическую клавиатуру.