Пирамида тестирования в CI
Updated Jul 2026
Структурирование этапов пайплайна на основе пирамиды тестирования
Не все тесты должны запускаться на одном этапе. Пирамида тестирования определяет не только то, какие тесты вы пишете, но и когда они запускаются в вашем пайплайне. Структурируйте пайплайн так, чтобы обеспечить быструю обратную связь сначала и дорогую валидацию позже.
/\
/ \
/ E2E \ Минуты | При слиянии в main
/--------\
/ \
/ Integration \ Минуты | При pull request
/--------------\
/ \
/ Unit Tests \ Секунды | При каждом push
/--------------------\
Этап 1: При каждом push (секунды-минуты)
Это самый быстрый цикл обратной связи. Разработчики получают результаты до того, как переключат контекст с только что написанного кода.
Что запускается здесь:
- Линтинг и статический анализ (ESLint, Pylint, Ruff)
- Проверка типов (TypeScript
tsc --noEmit, mypy) - Unit-тесты с отчётом о покрытии
- Проверка форматирования (Prettier, Black)
Почему именно эти, а не другие:
- Они быстрые (обычно менее 2 минут)
- Не требуют внешних сервисов (базы данных, API)
- Ловят самые распространённые ошибки (синтаксические ошибки, несоответствия типов, нарушения логики)
# Example: Fast feedback job
lint-and-unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm run test:unit -- --coverage
- uses: actions/upload-artifact@v4
if: always()
with:
name: coverage
path: coverage/lcov.info
Целевое время: менее 3 минут. Если линтинг и unit-тесты занимают больше, разработчики перестанут их дожидаться.
Этап 2: При pull request / слиянии в develop (минуты)
Этот этап запускается, когда код предлагается для интеграции. Он ловит проблемы, требующие реальной инфраструктуры — баз данных, очередей сообщений, контрактов внешних сервисов.
Что запускается здесь:
- Интеграционные тесты на реальных базах данных и сервисах
- Контрактные тесты API (Pact, Schemathesis)
- Компонентные тесты (отдельные сервисы или модули, тестируемые с реальными зависимостями)
- Сканирование безопасности (SAST-инструменты: Snyk, Semgrep)
# Example: Integration test job with database service
integration-tests:
needs: lint-and-unit
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: test_db
POSTGRES_PASSWORD: testpass
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run db:migrate
env:
DATABASE_URL: postgres://postgres:testpass@localhost:5432/test_db
- run: npm run test:integration
env:
DATABASE_URL: postgres://postgres:testpass@localhost:5432/test_db
REDIS_URL: redis://localhost:6379
Целевое время: менее 10 минут. Именно этот этап наиболее подвержен замедлению — следите за ним внимательно.
Этап 3: При слиянии в main / перед развёртыванием (минуты-десятки минут)
Это финальная проверка перед тем, как код попадёт к пользователям. Запускайте здесь дорогие, тщательные тесты.
Что запускается здесь:
- Полный набор браузерных тестов на нескольких браузерах
- Визуальные регрессионные тесты (визуальные сравнения Playwright, Percy, Chromatic)
- Дымовые тесты производительности (Lighthouse, k6 с базовыми порогами)
- Аудиты доступности (axe-core, Pa11y)
# Example: Cross-browser E2E tests with sharding
browser-tests:
needs: integration-tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
browser: [chromium, firefox, webkit]
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-${{ hashFiles('package-lock.json') }}
- run: npx playwright install --with-deps ${{ matrix.browser }}
- run: npx playwright test --project=${{ matrix.browser }} --shard=${{ matrix.shard }}/4
- uses: actions/upload-artifact@v4
if: failure()
with:
name: traces-${{ matrix.browser }}-${{ matrix.shard }}
path: test-results/
Целевое время: менее 20 минут с параллелизацией. Без шардирования браузерные тесты легко занимают 45+ минут.
Этап 4: После развёртывания (непрерывно)
После развёртывания тестирование не прекращается. Проверки после развёртывания подтверждают, что приложение работает в реальном продакшен-окружении.
Что запускается здесь:
- Дымовые тесты развёрнутого окружения (критические пользовательские сценарии)
- Синтетический мониторинг (тесты по расписанию, имитирующие поведение пользователей)
- Канареечный анализ (сравнение частоты ошибок между старой и новой версиями)
- Эндпоинты проверки здоровья
# Example: Post-deploy smoke tests
smoke-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx playwright test --project=smoke
env:
BASE_URL: https://production.example.com
- name: Notify on failure
if: failure()
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
-H 'Content-Type: application/json' \
-d '{"text":"Production smoke tests failed after deploy!"}'
Распространённые ошибки в структуре тестов пайплайна
Запуск всего при каждом push
Если браузерные тесты запускаются при каждом push, разработчики ждут 20 минут обратной связи по однострочному исправлению. Запускайте на push только быстрые тесты; дорогие тесты сохраняйте для событий PR и слияния.
Пропуск интеграционного уровня
Команды часто переходят от unit-тестов к браузерным тестам, пропуская интеграционный уровень. Это означает, что ошибки в запросах к базе данных, контрактах API или взаимодействиях сервисов ловятся только медленными, хрупкими E2E-тестами.
Недостаточно быстрый отказ
Если unit-тесты сломаны, не тратьте вычислительные ресурсы на запуск интеграционных и браузерных тестов. Используйте зависимости задач (needs), чтобы создать пайплайн, где каждый этап блокирует следующий.
Идентичные тесты на нескольких этапах
Если ваши интеграционные тесты уже проверяют API авторизации, браузерным тестам не нужно повторно тестировать авторизацию через API. Браузерные тесты должны фокусироваться на поведении UI, которое невозможно проверить на нижних уровнях.
Сопоставление типов тестов с этапами пайплайна
| Тип тестов | Этап | Триггер | Типичная длительность | Назначение |
|---|---|---|---|---|
| Линтинг / Проверка типов | 1 | Каждый push | 30с - 1мин | Поиск синтаксических и типовых ошибок |
| Unit-тесты | 1 | Каждый push | 30с - 3мин | Проверка логики в изоляции |
| Интеграционные тесты | 2 | PR / слияние в develop | 3 - 10мин | Проверка взаимодействия компонентов |
| Контрактные тесты | 2 | PR / слияние в develop | 1 - 5мин | Проверка соответствия API-схем |
| Браузерные тесты | 3 | Слияние в main | 5 - 20мин | Проверка сквозных пользовательских сценариев |
| Визуальная регрессия | 3 | Слияние в main | 3 - 10мин | Обнаружение непреднамеренных изменений UI |
| Тесты производительности | 3 | Слияние в main | 5 - 15мин | Проверка бюджетов времени отклика |
| Дымовые тесты | 4 | После развёртывания | 1 - 3мин | Проверка критических путей в продакшене |
| Синтетический мониторинг | 4 | По расписанию | 1 - 5мин | Постоянный контроль здоровья продакшена |
Практическое упражнение
- Сопоставьте ваш текущий набор тестов с четырьмя этапами пайплайна. На каком этапе находится каждый тип тестов? Есть ли пропущенные этапы?
- Измерьте длительность каждого этапа. Превышает ли какой-либо этап целевое время?
- Выявите тесты, которые запускаются на неправильном этапе (например, медленные тесты при каждом push)
- Создайте пайплайн с как минимум тремя этапами с зависимостями между задачами
- Убедитесь, что сбой на этапе 1 предотвращает запуск этапов 2 и 3