Files
telegram-helper-bot/.cursor/rules/testing.md
Andrey d2d7c83575 Обновлен Python до версии 3.11.9 и изменены зависимости в Dockerfile и pyproject.toml. Удалены устаревшие файлы RATE_LIMITING_SOLUTION.md и тесты для rate limiting.
Обновлены пути к библиотекам в Dockerfile для соответствия новой версии Python.
Исправлены все тесты, теперь все проходят
2026-01-25 16:07:27 +03:00

5.2 KiB
Raw Blame History

description, globs
description globs
Паттерны тестирования, структура тестов и использование pytest
tests/**/*.py
test_*.py

Паттерны тестирования

Структура тестов

Тесты находятся в директории 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

  1. Используйте фикстуры для переиспользования setup/teardown
  2. Изолируйте тесты - каждый тест должен быть независимым
  3. Используйте временные БД для тестов репозиториев
  4. Мокируйте внешние зависимости (API, файловая система)
  5. Пишите понятные имена тестов - они должны описывать что тестируется
  6. Используйте Arrange-Act-Assert паттерн
  7. Тестируйте граничные случаи и ошибки