Обновлены пути к библиотекам в Dockerfile для соответствия новой версии Python. Исправлены все тесты, теперь все проходят
5.2 KiB
5.2 KiB
description, globs
| description | globs | ||
|---|---|---|---|
| Паттерны тестирования, структура тестов и использование pytest |
|
Паттерны тестирования
Структура тестов
Тесты находятся в директории tests/ и используют pytest:
tests/
├── conftest.py # Общие фикстуры
├── conftest_*.py # Специализированные фикстуры
├── mocks.py # Моки и заглушки
└── test_*.py # Тестовые файлы
Конфигурация pytest
Настройки в pyproject.toml:
asyncio-mode=auto- автоматический режим для async тестов- Маркеры:
asyncio,slow,integration,unit - Фильтрация предупреждений
Структура теста
import pytest
from database.async_db import AsyncBotDB
from database.repositories.user_repository import UserRepository
@pytest.mark.asyncio
async def test_user_repository_add_user(db_path):
"""Тест добавления пользователя."""
# Arrange
repo = UserRepository(db_path)
user = User(user_id=123, full_name="Test User")
# Act
await repo.add_user(user)
# Assert
result = await repo.get_user_by_id(123)
assert result is not None
assert result.full_name == "Test User"
Фикстуры
Общие фикстуры (conftest.py)
import pytest
import tempfile
import os
@pytest.fixture
def db_path():
"""Создает временный файл БД для тестов."""
fd, path = tempfile.mkstemp(suffix='.db')
os.close(fd)
yield path
os.unlink(path)
@pytest.fixture
async def async_db(db_path):
"""Создает AsyncBotDB для тестов."""
db = AsyncBotDB(db_path)
await db.create_tables()
yield db
Использование фикстур
@pytest.mark.asyncio
async def test_something(async_db):
# async_db уже инициализирован
result = await async_db.some_method()
assert result is not None
Моки
Используйте mocks.py для общих моков:
from unittest.mock import AsyncMock, MagicMock
def mock_bot():
"""Создает мок бота."""
bot = MagicMock()
bot.send_message = AsyncMock()
return bot
Маркеры
Используйте маркеры для категоризации тестов:
@pytest.mark.unit
@pytest.mark.asyncio
async def test_unit_test():
"""Быстрый unit тест."""
...
@pytest.mark.integration
@pytest.mark.asyncio
async def test_integration_test():
"""Медленный integration тест."""
...
@pytest.mark.slow
@pytest.mark.asyncio
async def test_slow_test():
"""Медленный тест."""
...
Запуск с фильтрацией:
pytest -m "not slow" # Пропустить медленные тесты
pytest -m unit # Только unit тесты
Тестирование Handlers
from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage
from unittest.mock import AsyncMock
@pytest.mark.asyncio
async def test_handler(mock_bot, mock_message):
"""Тест handler."""
# Arrange
dp = Dispatcher(storage=MemoryStorage())
# Регистрация handler
...
# Act
await dp.feed_update(mock_update)
# Assert
mock_bot.send_message.assert_called_once()
Тестирование Services
@pytest.mark.asyncio
async def test_service_method(async_db):
"""Тест метода сервиса."""
service = SomeService(async_db, {})
result = await service.do_something()
assert result is not None
Тестирование Repositories
@pytest.mark.asyncio
async def test_repository_crud(db_path):
"""Тест CRUD операций репозитория."""
repo = SomeRepository(db_path)
await repo.create_tables()
# Create
entity = SomeEntity(...)
await repo.add(entity)
# Read
result = await repo.get_by_id(entity.id)
assert result is not None
# Update
entity.field = "new_value"
await repo.update(entity)
# Delete
await repo.delete(entity.id)
result = await repo.get_by_id(entity.id)
assert result is None
Best Practices
- Используйте фикстуры для переиспользования setup/teardown
- Изолируйте тесты - каждый тест должен быть независимым
- Используйте временные БД для тестов репозиториев
- Мокируйте внешние зависимости (API, файловая система)
- Пишите понятные имена тестов - они должны описывать что тестируется
- Используйте Arrange-Act-Assert паттерн
- Тестируйте граничные случаи и ошибки