Фабрика тестовых данных

Фабрики данных создаются по принципам ООП и этой статье будет рассматриваться в контексте тестирования REST API. Одна фабрика соответствует одной схеме API.

Рассмотрим на примере схемы profile (api-service). В этой схеме нужно создавать профиль пользователя, для этого нужны данные их можно сгенерировать с помощью faker или создать свой ручной набор (если нужна строгость в плане КЭ и ГЗ).

Класс-фабрика состоит из нескольких частей:

Это демонстрационный минимум, список можно расширять сколько угодно.

Пример фабрики профилей пользователя

class ProfileFactory:
    """
    Фабрика для создания тестовых данных профилей пользователей
    в точном соответствии с реальной структурой ответа API.
    """

    EDU_LEVELS = [
        'nothing',
        'school_9',
        'school_11',
        ...
    ]

    def __init__(self, locale: str = "en_US", config_path: str | None = None):
        self.fake = Faker(locale)
        self._manual_data: list[dict[str, Any]] = []
        if config_path:
            self.load_manual_data(config_path)

    def load_manual_data(self, config_path: str) -> None:
        """Загружает ручные профили из JSON- или YAML-файла."""
        import json
        with open(config_path, "r", encoding="utf-8") as f:
            if config_path.endswith(('.yaml', '.yml')):
                import yaml
                self._manual_data = yaml.safe_load(f)
            else:
                self._manual_data = json.load(f)

    def create_profile(self, **overrides) -> dict[str, Any]:
        """
        Создаёт один профиль с опциональными переопределениями полей.
        Все поля приведены к структуре API.
        """
        user_id = random.randint(1, 99999)

        profile = {
            "first_name": self.fake.first_name(),
            "middle_name": self._optional_field(self.fake.first_name, 0.3),
            "last_name": self.fake.last_name(),
            "profession": self.fake.job(),
            "city": self.fake.city(),
            "country": self.fake.country(),
            "relocation": f"{self.fake.country()}, {self.fake.city()}",
            "edu_level": random.choice(self.EDU_LEVELS),
            "institution_name": self.fake.company(),
            "graduation_year": random.randint(2000, 2026),
            "short_desc": self.fake.sentence(nb_words=10)[:100],
            "full_desc": self.fake.text(max_nb_chars=500),
            "wallpaper": None,
            "avatar": None,
            "link_to_instagram": self._optional_field(self.fake.url, 0.7),
            "link_to_telegram": self._optional_field(self.fake.url, 0.7),
            "link_to_github": self._optional_field(self.fake.url, 0.7),
            "link_to_vk": self._optional_field(self.fake.url, 0.7),
        }

        profile.update(overrides)
        return profile

    def generate_profiles(
        self,
        count: int = 1,
        use_manual: bool = False,
        **overrides,
    ) -> list[dict[str, Any]]:
        """
        Возвращает список профилей заданной длины.
        """

        profiles = [self.create_profile() for _ in range(count)]

        if overrides:
            for p in profiles:
                p.update(overrides)
        return profiles

В идеале можно использовать модели данных из реального API и на его основе формировать список параметров и типов данных для профиля. Но это уже программа максимум и если целесообразно плотная интеграция с кодом разработки (возможно, создавать отдельную фабрику имеет больше смысла, так как модель могут несанкционированно изменить и пользователь перестанет иметь доступ к части своих данных после очередного релиза).