Пирамида тестирования | QArtel
Ко всей базеautomation
Пирамида тестирования Около 7 мин чтения
Назначение
Пирамида тестирования — это модель, предложенная Майком Коном (Mike Cohn) в книге Succeeding with Agile (2009), которая описывает оптимальное соотношение между разными уровнями автоматизированных тестов. Она помогает команде понять, сколько тестов какого типа нужно писать и как они соотносятся по скорости, стоимости и надёжности.
Классическая пирамида
/\
/ \ E2E (End-to-End)
/ E2E\ —небольшое количество, дорогие, медленные
/------\
/ \ Integration
/Integration\ — среднее количество
/------------\
/ \ Unit
/ Unit Tests \ — много, быстрые, дешёвые
/__________________\
Соотношение (классическое)
Уровень Доля Скорость Стоимость Надёжность Unit
Integration 20-30% Секунды Средняя Средняя
E2E 5-10% Минуты Высокая Низкая (фликеры)
Уровни пирамиды
1. Unit-тесты (модульные) Что тестируют: Отдельные функции, методы, классы в изоляции.
⚡ Быстрые: < 1 мс на тест
📦 Изолированные: не зависят от БД, сети, файловой системы
🔧 Легко пишутся и поддерживаются
📍 Точно указывают на причину падения
Бизнес-логику (вычисления, валидация, условия)
Преобразования данных (маппинг, сериализация)
Граничные случаи (null, пустые значения, макс/мин)
Обработку ошибок
Примеры (Python + pytest):
def test_calculate_discount_regular_user():
assert calculate_discount(total=1000, user_type="regular") == 0
def test_calculate_discount_vip_user():
assert calculate_discount(total=1000, user_type="vip") == 100
def test_calculate_discount_boundary():
assert calculate_discount(total=0, user_type="vip") == 0
def test_calculate_discount_negative():
with pytest.raises(ValueError):
calculate_discount(total=-100, user_type="regular")
Примеры (JavaScript + Jest):
describe('formatDate', () => {
test('formats date correctly', () => {
expect(formatDate(new Date(2024, 5, 10))).toBe('10.06.2024');
});
test('handles null input', () => {
expect(formatDate(null)).toBe('');
});
});
2. Интеграционные тесты Что тестируют: Взаимодействие между компонентами: API ↔ БД, сервис ↔ сервис, модуль ↔ модуль.
🕐 Средняя скорость: 100 мс — 5 сек
🔗 Зависят от внешних ресурсов (БД, API)
📊 Проверяют контракты между компонентами
⚠️ Могу быть хрупкими при изменениях в зависимостях
API-запросы и ответы (статус-код, тело, заголовки)
Работу с БД (запись, чтение, миграции)
Взаимодействие микросервисов
Очереди сообщений (Kafka, RabbitMQ)
Кэширование (Redis)
Примеры (Python + pytest):
# API-тест
def test_create_user_api(client, db_session):
response = client.post("/api/users", json={
"name": "Тест",
"email": "test@example.com"
})
assert response.status_code == 201
assert response.json()["name"] == "Тест"
# Проверяем в БД
user = db_session.query(User).filter_by(email="test@example.com").first()
assert user is not None
assert user.name == "Тест"
# Тест с реальной БД (testcontainers)
@pytest.fixture
def db_session():
container = PostgresContainer("postgres:15")
container.start()
engine = create_engine(container.get_connection_url())
Base.metadata.create_all(engine)
session = Session(engine)
yield session
session.close()
container.stop()
Примеры (JavaScript + supertest):
describe('POST /api/users', () => {
test('creates user and returns 201', async () => {
const res = await request(app)
.post('/api/users')
.send({ name: 'Тест', email: 'test@example.com' });
expect(res.status).toBe(201);
expect(res.body.name).toBe('Тест');
});
});
3. E2E-тесты (End-to-End) Что тестируют: Полные пользовательские сценарии через UI (или API-цепочки).
🐌 Медленные: 5 сек — 5 мин на тест
💰 Дорогие в поддержке
🎯 Проверяют реальный пользовательский опыт
⚠️ Фликеры (зависят от UI, сети, состояния)
Критичные бизнес-флоу (регистрация, оплата, checkout)
Интеграцию всех слоёв (UI → API → БД → внешние сервисы)
Пользовательские сценарии (user journeys)
from playwright.sync_api import sync_playwright
def test_complete_purchase():
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
# Авторизация
page.fill('[data-testid="email"]', 'user@example.com')
page.fill('[data-testid="password"]', 'password123')
page.click('[data-testid="login-btn"]')
# Добавление в корзину
page.click('[data-testid="product-1"]')
page.click('[data-testid="add-to-cart"]')
# Оформление заказа
page.click('[data-testid="checkout"]')
page.fill('[data-testid="address"]', 'ул. Пушкина, д. 1')
page.click('[data-testid="confirm-order"]')
# Проверка
assert page.inner_text('[data-testid="order-status"]') == "Заказ оформлен"
browser.close()
Вариации пирамиды
Тестовый бриллиант (Test Diamond) /\
/ \ E2E
/ E2E \ —небольшое количество
/------\
/ \ Integration
/Integration\ — много (акцент на интеграцию)
\--------/
\ / Unit
\Unit / —небольшое количество (если мало бизнес-логики)
\ /
\/
Микросервисная архитектура
Много интеграций между сервисами
Мало сложной бизнес-логики в каждом сервисе
Тестовая песочные часы (Test Hourglass) /\
/ \ E2E — много
/ E2E \
/------\
\ /
\/ Unit — мало
/ \
/Unit\ (только критичная логика)
/------\
\ /
\/ \/ Integration — много
/Int\ (основной фокус)
/______\
Legacy-системы с большим количеством интеграций
Системы с простой бизнес-логикой, но сложными потоками данных
Тестовая перевёрнутая пирамида (Anti-pattern) /__________________\
\ /
\ E2E Tests / — МНОГО E2E-тестов
\ / — долго, дорого, фликеры
\------------/
\ /
\Integration/ — мало интеграционных
\--------/
\ /
\Unit/ — мало unit-тестов
\/
E2E-тесты медленные → CI пайплайн занимает часы
Фликеры → команда теряет доверие к тестам
Дорого в поддержке → тесты ломаются при каждом изменении UI
Не показывают причину падения
Практические примеры
Пример 1: Интернет-магазин Уровень Что тестировать Количество Unit Расчёт скидки, валидация корзины, форматирование цены, расчёт доставки 50+ Integration API товаров, API корзины, API заказов, интеграция с платёжной системой 20+ E2E Полный сценарий: поиск → корзина → оплата → подтверждение 5-10
Пример 2: Мобильное приложение (банк) Уровень Что тестировать Количество Unit Расчёт процентов, валидация перевода, форматирование баланса 80+ Integration API переводов, API истории, push-уведомления, биометрия 15+ E2E Перевод между счетами, оплата услуг, просмотр выписки 5-8
Пример 3: CRM-система Уровень Что тестировать Количество Unit Лид-скоринг, расчёт воронки, маппинг данных, права доступа 60+ Integration API сделок, API контактов, email-интеграция, импорт из Excel 25+ E2E Создание лида → сделка → закрытие, массовый импорт 8-12
Практические рекомендации
1. Пишите тесты снизу вверх 1. Unit — при написании кода (TDD)
2. Integration — при разработке API/сервисов
3. E2E — для критичных пользовательских сценариев
2. Следуйте правилу «Тестового треугольника» - Unit-тестов ДОЛЖНО быть больше всех
- Integration-тестов — средне
- E2E-тестов — минимум (только критичные флоу)
3. Используйте тестовые пирамиды для разных уровней Фронтенд-пирамида:
Unit (Jest): компоненты, хуки, утилиты
Integration (Testing Library): компоненты с моками
E2E (Playwright/Cypress): критичные флоу
Бэкенд-пирамида:
Unit (pytest/JUnit): сервисы, бизнес-логика
Integration (testcontainers): API + БД
E2E (docker-compose): полная система
4. Мониторьте здоровье тестов Метрика Целевое значение Время CI-пайплайна < 15 минут Процент фликеров < 2% Покрытие кода (unit) > 80% Покрытие API (integration) > 90% критичных эндпоинтов E2E-тесты на критичные флоу 100%
Связанные концепции
Тестовый куб (Test Cube) Расширение пирамиды для микросервисов: три измерения — глубина (unit → E2E), ширина (функциональность), высота (среда: dev → staging → prod).
Trophy (Majestic Testing Trophy) Предложена Kent C. Dodds: акцент на интеграционных тестах.
🏆
/ \
/ E2E\ —небольшое количество
/------\
/ \
/Static \ — Static analysis (lint, types)
/Analysis \
/--------------\
/ \
/ Integration \ — ОСНОВНОЙ ФОКУС
\ Tests /
\--------------/
\ /
\ Unit / —небольшое количество (только критичная логика)
\ Tests /
\----/
Философия: Не тратить время на unit-тесты тривиального кода. Писать интеграционные тесты, которые проверяют реальное поведение.
Итог Вопрос Ответ Сколько unit-тестов? Больше всего (60-70%) Сколько E2E? Минимум (5-10%), только критичные флоу Где основной фокус? На интеграционных тестах (особенно в микросервисах) Что писать первым? Unit (TDD), потом integration, потом E2E Что не писать? Не дублировать проверки на разных уровнях