Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Типы тестов без Mock'ирования
Тесты без mock'ирования взаимодействуют с реальными зависимостями — БД, файловой системой, внешними API. Они имеют разные названия в зависимости от уровня и контекста.
Integration Tests (Интеграционные тесты)
Определение — тесты, которые проверяют взаимодействие нескольких компонентов вместе.
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# Используем тестовую БД, а не mock
TEST_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(TEST_DATABASE_URL)
TestingSessionLocal = sessionmaker(bind=engine)
@pytest.fixture
def db():
# Создаём таблицы
Base.metadata.create_all(bind=engine)
session = TestingSessionLocal()
yield session
session.close()
# Очищаем таблицы
Base.metadata.drop_all(bind=engine)
def test_create_and_get_user(db):
# Интеграционный тест — БД реальная
user_service = UserService(db)
# Создаём пользователя
user = user_service.create_user(name="John", email="john@example.com")
# Получаем пользователя
retrieved_user = user_service.get_user(user.id)
assert retrieved_user.name == "John"
assert retrieved_user.email == "john@example.com"
End-to-End Tests (E2E тесты)
Определение — тесты, которые проверяют полный сценарий пользователя от начала до конца, включая UI.
# Playwright для веба
from playwright.async_api import async_playwright
async def test_user_registration_flow():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
# Переходим на сайт
await page.goto("http://localhost:3000/register")
# Заполняем форму
await page.fill("input[name='email']", "user@example.com")
await page.fill("input[name='password']", "password123")
# Отправляем форму
await page.click("button[type='submit']")
# Проверяем успешность
await page.wait_for_url("http://localhost:3000/dashboard")
assert await page.is_visible("text=Welcome")
await browser.close()
# Для Telegram Bot — E2E через dispatcher
import pytest
from aiogram.types import Update, Message, Chat, User as TgUser
from aiogram.types import CallbackQuery
@pytest.mark.asyncio
async def test_bot_full_flow(dp, bot):
# E2E тест бота — реальный диспетчер
update = Update(
update_id=1,
message=Message(
message_id=1,
date=datetime.now(),
chat=Chat(id=123, type="private"),
from_user=TgUser(id=123, is_bot=False, first_name="Test"),
text="/start"
)
)
# Отправляем в реальный диспетчер
await dp.feed_update(bot, update)
# Проверяем результат
# ...
Acceptance Tests (Приёмочные тесты)
Определение — тесты, написанные с точки зрения пользователя/бизнеса, проверяют требования.
# BDD стиль (Behavior Driven Development)
import pytest
class TestUserManagement:
"""Как администратор, я хочу управлять пользователями"""
def test_list_active_users(self, db):
# GIVEN: в системе есть активные пользователи
user_service = UserService(db)
user_service.create_user(name="Alice", active=True)
user_service.create_user(name="Bob", active=True)
user_service.create_user(name="Carol", active=False)
# WHEN: я запрашиваю список активных пользователей
active_users = user_service.list_active_users()
# THEN: я получу только активных
assert len(active_users) == 2
assert all(u.active for u in active_users)
Smoke Tests (Дымовые тесты)
Определение — быстрые базовые тесты, проверяют что система вообще работает.
def test_api_is_up():
"""Быстрый smoke test"""
response = requests.get("http://localhost:8000/health")
assert response.status_code == 200
assert response.json()["status"] == "ok"
def test_database_connection():
"""Проверяем БД доступна"""
engine = create_engine(DATABASE_URL)
with engine.connect() as conn:
result = conn.execute(text("SELECT 1"))
assert result.fetchone()[0] == 1
System Tests (Системные тесты)
Определение — тесты всей системы в контексте, приближенные к production.
# docker-compose с реальными зависимостями
@pytest.mark.integration
def test_full_order_workflow(db, redis_client, elasticsearch):
# Реальная БД, Redis, Elasticsearch
order_service = OrderService(db, redis_client, elasticsearch)
# Создаём заказ
order = order_service.create_order(
user_id=123,
items=[{"product_id": 1, "quantity": 2}]
)
# Проверяем что в Elasticsearch проиндексировано
assert elasticsearch.exists(index="orders", id=order.id)
# Проверяем кеш в Redis
cached_order = redis_client.get(f"order:{order.id}")
assert cached_order is not None
Regression Tests (Регрессионные тесты)
Определение — тесты, проверяющие что старые ошибки не вернулись после изменений.
def test_issue_123_fixed():
"""Проверяем что bug #123 не вернулся"""
# До исправления это падало
user = User(name="Alice", email="")
with pytest.raises(ValidationError):
user.validate() # Должно выбросить ошибку
Canary Tests (Канареечные тесты)
Определение — тесты на production или pre-production окружении для проверки что всё работает.
import requests
def test_canary_production():
"""Проверяем production API"""
response = requests.get("https://api.production.com/health")
assert response.status_code == 200
# Проверяем критичные функции
response = requests.post(
"https://api.production.com/api/v1/payments/charge",
json={"user_id": 999999, "amount": 1}, # фиктивный платёж
headers={"Authorization": f"Bearer {TEST_TOKEN}"}
)
assert response.status_code == 200
Различие между типами
| Тип | Использует mock | Использует реальные зависимости | Скорость | Надёжность |
|---|---|---|---|---|
| Unit | ✓ | ✗ | Быстро | Среднее |
| Integration | ✗ | ✓ | Медленно | Хорошо |
| E2E | ✗ | ✓ | Очень медленно | Отличное |
| Acceptance | Зависит | Зависит | Медленно | Хорошо |
| Smoke | ✗ | ✓ | Очень быстро | Среднее |
Пирамида тестирования
╱╲ E2E (5%)
╱ ╲ Acceptance (10%)
╱────╲ Integration (25%)
╱ ╲ Unit (60%)
Много быстрых unit тестов (с mock), меньше интеграционных, ещё меньше E2E.
Лучшие практики
Unit тесты с mock для скорости и изоляции. Integration тесты для критичных компонентов (БД, сервисы). E2E для main flow (регистрация, оплата). Используй testcontainers для БД/Redis в интеграционных тестах. Параллели интеграционные тесты если возможно. Фиксируй flaky тесты (нестабильные).