Перейти к содержанию

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

В этом разделе описано, как тестировать код, использующий библиотеку FUCKTAR, а также как тестировать саму библиотеку.

Тестирование кода, использующего FUCKTAR

Базовое тестирование генерации

import pytest
from fucktar.generator import BasePattern
from fucktar.config import PatternConfig
from fucktar.default_patterns.users import UserRuPatterns

# Модель данных
@dataclass
class User:
    first_name: str
    last_name: str
    email: str

# Паттерн генерации
class UserPatterns(BasePattern[User]):
    first_name = UserRuPatterns.first_name
    last_name = UserRuPatterns.last_name
    email = UserRuPatterns.email

    pattern_config = PatternConfig(
        scope="test_users",
        unique_fields=["email"]
    )

# Тест
def test_user_generation():
    """Тест генерации пользователей"""
    user = UserPatterns().generate()

    # Проверяем, что объект создан
    assert user is not None

    # Проверяем типы полей
    assert isinstance(user.first_name, str)
    assert isinstance(user.last_name, str)
    assert isinstance(user.email, str)

    # Проверяем, что поля не пустые
    assert len(user.first_name) > 0
    assert len(user.last_name) > 0
    assert len(user.email) > 0

Тестирование уникальности

def test_unique_user_generation():
    """Тест генерации уникальных пользователей"""
    users = UserPatterns().configure(unique=True).generate(count=100)

    # Проверяем количество
    assert len(users) == 100

    # Проверяем уникальность email
    emails = [user.email for user in users]
    assert len(set(emails)) == len(emails)

Тестирование значений по умолчанию

def test_default_values():
    """Тест установки значений по умолчанию"""
    users = UserPatterns().default_values(city="Москва").generate(count=10)

    # Проверяем, что значение по умолчанию установлено
    for user in users:
        # Предполагается, что city - это поле в модели User
        assert user.city == "Москва"

Тестирование многопоточности

import concurrent.futures

def test_multithreaded_generation():
    """Тест многопоточной генерации"""
    def generate_batch(count):
        return UserPatterns().configure(unique=True).generate(count=count)

    # Генерация в нескольких потоках
    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
        futures = [executor.submit(generate_batch, 25) for _ in range(4)]
        results = [future.result() for future in futures]

    # Проверяем результаты
    all_users = [user for batch in results for user in batch]

    # Проверяем общее количество
    assert len(all_users) == 100

    # Проверяем уникальность
    emails = [user.email for user in all_users]
    assert len(set(emails)) == len(emails)

Очистка истории в тестах

Важно очищать историю между тестами для избежания влияния одного теста на другой:

import pytest
from fucktar.storage import ScopeStorage

@pytest.fixture(autouse=True)
def cleanup():
    """Очистка перед каждым тестом"""
    ScopeStorage.clear_all()
    yield
    ScopeStorage.clear_all()

Тестирование ошибок

Тестирование таймаута

import pytest
from fucktar.exceptions import TimeoutGenerationError

def test_timeout_generation():
    """Тест таймаута генерации"""
    # Создаем паттерн, который будет долго генерироваться
    class SlowPattern(BasePattern[User]):
        first_name = r"[A-z]{1000}"
        last_name = r"[A-z]{1000}"
        email = UserRuPatterns.email

        pattern_config = PatternConfig(
            scope="slow", 
            unique_fields=["email"]
        )

    gen = SlowPattern().configure(unique=True)

    # Ожидаем исключение таймаута
    with pytest.raises(TimeoutGenerationError):
        gen.generate(count=100000, timeout=1)

Тестирование ошибок уникальности

import pytest
from fucktar.exceptions import UniqueGenerationError

def test_unique_generation_error():
    """Тест ошибки генерации уникальных данных"""
    # Создаем паттерн с очень ограниченным набором значений
    class LimitedPattern(BasePattern[User]):
        first_name = r"[0-9]"  # Очень ограничительный паттерн
        last_name = r"[0-9]"
        email = r"[0-9]@test.com"

        pattern_config = PatternConfig(
            scope="limited", 
            unique_fields=["first_name"]
        )

    gen = LimitedPattern().configure(unique=True)

    # Ожидаем исключение уникальности
    with pytest.raises(UniqueGenerationError):
        gen.generate(count=100, max_attempts=5)

Тестирование пользовательских паттернов

def test_custom_pattern():
    """Тест пользовательского паттерна"""
    @dataclass
    class Product:
        name: str
        price: str
        category: str

    class ProductPatterns(BasePattern[Product]):
        name = r"^[A-Z][a-z]{5,20} [A-Z][a-z]{3,15}$"
        price = r"^\d{1,4}\.\d{2}$"
        category = r"^(electronics|clothing|books|home)$"

        pattern_config = PatternConfig(
            scope="products",
            unique_fields=["name"]
        )

    products = ProductPatterns().generate(count=10)

    # Проверяем структуру данных
    for product in products:
        assert isinstance(product.name, str)
        assert isinstance(product.price, str)
        assert isinstance(product.category, str)

        # Проверяем формат цены
        assert "." in product.price
        parts = product.price.split(".")
        assert len(parts) == 2
        assert len(parts[1]) == 2

Мокирование для тестов

В некоторых случаях может быть полезно мокировать генерацию для тестирования логики приложения:

from unittest.mock import patch

def test_with_mocked_generation():
    """Тест с мокированной генерацией"""
    with patch('my_module.UserPatterns.generate') as mock_generate:
        # Настраиваем мок
        mock_generate.return_value = User(
            first_name="Иван",
            last_name="Иванов",
            email="ivan@example.com"
        )

        # Вызываем тестируемую функцию
        result = my_function_that_uses_user_generation()

        # Проверяем результат
        assert result is not None
        mock_generate.assert_called_once()

Рекомендации по тестированию

  1. Всегда очищайте историю между тестами
  2. Тестируйте как успешные сценарии, так и сценарии с ошибками
  3. Используйте параметризованные тесты для проверки различных конфигураций
  4. Тестируйте многопоточность отдельно
  5. Используйте фикстуры для повторяющейся логики настройки
  6. Пишите тесты для пользовательских паттернов и расширений
  7. Проверяйте корректность типов сгенерированных данных
  8. Тестируйте граничные условия (например, генерацию 0 или 1 объекта)