Тестирование¶
В этом разделе описано, как тестировать код, использующий библиотеку 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()
Рекомендации по тестированию¶
- Всегда очищайте историю между тестами
- Тестируйте как успешные сценарии, так и сценарии с ошибками
- Используйте параметризованные тесты для проверки различных конфигураций
- Тестируйте многопоточность отдельно
- Используйте фикстуры для повторяющейся логики настройки
- Пишите тесты для пользовательских паттернов и расширений
- Проверяйте корректность типов сгенерированных данных
- Тестируйте граничные условия (например, генерацию 0 или 1 объекта)