← Назад к вопросам

Что такое пирамида тестирования?

1.0 Junior🔥 301 комментариев
#Теория тестирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

Пирамида тестирования — это концепция, которая показывает оптимальное распределение тестов по их типам и уровням. Впервые её популяризировал Mike Cohn в своей книге "Succeeding with Agile".

Классическая пирамида (3 уровня)

         ^^
        /  \
       / E2E \           5%    (1-2 часа выполнения)
      /______\
     /        \
    /Integration\        15%   (15-30 минут)
   /___________\
  /             \
 /   Unit Tests   \     80%   (секунды)
/__________________\

Уровни пирамиды

1. Unit Tests (основание, 80%)

Что: тесты отдельных функций/методов в изоляции.

Преимущества:

  • Очень быстрые (выполняются за ms)
  • Дешёвые в поддержке
  • Много их можно написать
  • Выявляют баги на ранних стадиях
  • Просто отладить при падении

Недостатки:

  • Не тестируют интеграцию компонентов
  • Не имитируют реальные сценарии

Пример:

import pytest
from calculator import Calculator

@pytest.mark.parametrize("a,b,expected", [
    (2, 3, 5),
    (10, -5, 5),
    (0, 0, 0),
])
def test_add(a, b, expected):
    calc = Calculator()
    assert calc.add(a, b) == expected

def test_divide_by_zero():
    calc = Calculator()
    with pytest.raises(ZeroDivisionError):
        calc.divide(10, 0)

Инструменты: pytest, unittest, Jest, Vitest

2. Integration Tests (середина, 15%)

Что: тесты взаимодействия нескольких компонентов (например, микросервис + база данных).

Преимущества:

  • Проверяют реальные взаимодействия
  • Выявляют проблемы интеграции
  • Выполняются относительно быстро

Недостатки:

  • Медленнее unit тестов
  • Требуют настройки окружения (БД, очереди, API моки)
  • Сложнее отладить при падении

Пример:

import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from app.models import User
from app.services import UserService

@pytest.fixture
def test_db():
    engine = create_engine('sqlite:///:memory:')
    Session.configure(bind=engine)
    Base.metadata.create_all(engine)
    return Session()

def test_create_user_in_database(test_db):
    """Тест взаимодействия UserService и БД"""
    service = UserService(test_db)
    user = service.create_user(name="Alice", email="alice@test.com")
    
    # Проверяем, что пользователь реально сохранён в БД
    saved_user = test_db.query(User).filter_by(email="alice@test.com").first()
    assert saved_user is not None
    assert saved_user.name == "Alice"

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

from fastapi.testclient import TestClient
from app import app

client = TestClient(app)

def test_get_users_api():
    response = client.get("/api/users")
    assert response.status_code == 200
    assert len(response.json()) > 0

def test_create_user_api():
    response = client.post("/api/users", json={
        "name": "Bob",
        "email": "bob@test.com"
    })
    assert response.status_code == 201
    assert response.json()["id"] is not None

Инструменты: pytest, requests, httpx, TestClient (FastAPI)

3. End-to-End Tests (вершина, 5%)

Что: тесты полного пользовательского сценария через интерфейс приложения.

Преимущества:

  • Тестируют реальный пользовательский сценарий
  • Выявляют проблемы, которые юнит и интеграционные не видят
  • Дают уверенность перед production deployment

Недостатки:

  • Очень медленные (1-5 минут за тест)
  • Нестабильные (flaky) из-за асинхронности
  • Дорогие в поддержке
  • Сложно отладить
  • Требуют запущенного приложения

Пример:

import pytest
from playwright.sync_api import sync_playwright

def test_complete_checkout_flow():
    """E2E тест: юзер добавляет товар в корзину и покупает"""
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        
        # Переход на сайт
        page.goto("https://shop.example.com")
        
        # Поиск товара
        page.fill("input[placeholder='Search']", "laptop")
        page.click("button[type='submit']")
        page.click("text=Laptop Pro")
        
        # Добавление в корзину
        page.click("button:has-text('Add to Cart')")
        page.click("a:has-text('View Cart')")
        
        # Оформление
        page.click("button:has-text('Proceed to Checkout')")
        page.fill("input[name='email']", "customer@test.com")
        page.fill("input[name='card']", "4111111111111111")
        page.click("button:has-text('Complete Purchase')")
        
        # Проверка
        assert "Order Confirmed" in page.text_content()
        
        browser.close()

Инструменты: Playwright, Cypress, Selenium, Puppeteer

Почему пирамида, а не куб?

Анти-паттерн: Ледяной конус (Ice Cream Cone)

        ^
       /|\
      / | \
     /E2E \
    /______\
   /        \
  / Integration
 /____________\
|              |  <- Много ручного тестирования
|  Manual Tests |
|______________|

Проблемы:

  • Медленная обратная связь (часы вместо секунд)
  • Дорого поддерживать
  • Мало unit тестов = много багов пропускают
  • Большинство тестов падают медленно (E2E)

Современная вариация: Пирамида с API слоем

В modern приложениях часто добавляют слой API тестов:

          /\
         /E2E\
        /______\
       /        \
      /   API    \      <- API Contract Testing, GraphQL
     /___________\
    /             \
   /Integration   \
  /________________\
 /                  \
 /    Unit Tests     \
/____________________\

API тесты:

  • Быстрее E2E, но медленнее unit
  • Проверяют контракт между сервисами
  • Идеальны для микросервисной архитектуры
import requests

def test_api_response_format():
    """Contract testing: проверяем формат API ответа"""
    response = requests.get("https://api.example.com/users/123")
    assert response.status_code == 200
    
    data = response.json()
    # Структура ответа должна быть точной
    assert "id" in data
    assert "name" in data
    assert "email" in data
    assert isinstance(data["id"], int)
    assert isinstance(data["name"], str)

Практические метрики пирамиды

ТипКол-воВремя выполнения
Unit80-85%Секунды (5-10 мин total)
Integration10-15%Минуты (10-20 мин total)
E2E5-10%~1 мин на тест (10-20 мин total)
Total100%~30-40 минут full suite

Как я применяю пирамиду в практике

Фаза 1: Coverage Analysis

# Смотрю current state
pytest --cov=app --cov-report=html
# Если покрытие < 70% → нужны unit тесты

Фаза 2: Построение base

# Сначала пишу unit тесты на критичную бизнес-логику
def test_calculate_price():
    """Unit: функция расчёта скидки"""
    assert calculate_discount(100, vip=False) == 100
    assert calculate_discount(100, vip=True) == 90

Фаза 3: Integration layer

# Затем тестирую взаимодействие с БД, API
def test_create_order_with_discount():
    """Integration: создание заказа в БД с применением скидки"""
    order = OrderService().create(amount=100, vip=True)
    assert order.final_price == 90

Фаза 4: E2E критичные пути

# И только для самых критичных путей — E2E
def test_user_can_purchase():
    """E2E: полный сценарий покупки через UI"""
    # ...

Красные флаги (когда пирамида сломана)

🚩 Много E2E тестов, мало unit тестов 🚩 Все тесты fall медленнее 5 минут 🚩 50%+ тестов flaky 🚩 Время на запуск full suite > 1 часа 🚩 Тесты никто не смотрит (CI зелёный всегда)

Заключение

Пирамида тестирования — не догма, а руководство:

  • Unit — фундамент, должны быть быстрыми и надёжными
  • Integration — проверяют реальные взаимодействия
  • E2E — страховка для критичных путей

Оптимальное распределение зависит от проекта, но правило простое: максимум быстрых тестов, минимум медленных.

Что такое пирамида тестирования? | PrepBro