Какой подход вы используете при создании фреймворка для автотестов с нуля?

Ответ

При создании фреймворка с нуля я фокусируюсь на модульности, поддерживаемости и интеграции в CI/CD. Вот пример каркаса на Python с pytest:

Структура проекта:

test_framework/
├── conftest.py              # Глобальные фикстуры pytest
├── pytest.ini               # Конфигурация pytest
├── requirements.txt         # Зависимости
├── config/
│   ├── __init__.py
│   ├── settings.py          # Чтение .env, константы
│   └── environments.py      # Настройки dev/stage/prod
├── core/
│   ├── __init__.py
│   ├── webdriver_factory.py # Singleton для управления браузером
│   └── api_client.py        # Обертка над requests
├── pages/                   # Page Object / Page Component
│   ├── __init__.py
│   ├── base_page.py
│   └── login_page.py
├── tests/
│   ├── __init__.py
│   ├── ui/
│   └── api/
└── utils/
    ├── data_generators.py
    └── allure_helpers.py

Ключевые компоненты и их реализация:

  1. Управление браузером (Factory + Singleton):
    
    # core/webdriver_factory.py
    from selenium.webdriver import Chrome, Firefox
    from selenium.webdriver.chrome.options import Options as ChromeOptions

class WebDriverFactory: _instance = None

def __new__(cls):
    if cls._instance is None:
        cls._instance = super().__new__(cls)
    return cls._instance

def get_driver(self, browser_name="chrome"):
    if browser_name.lower() == "chrome":
        options = ChromeOptions()
        options.add_argument("--headless=new")  # Для CI
        return Chrome(options=options)
    elif browser_name.lower() == "firefox":
        return Firefox()
    else:
        raise ValueError(f"Unsupported browser: {browser_name}")

2.  **Базовая фикстура для UI-тестов в `conftest.py`:**
```python
import pytest
from core.webdriver_factory import WebDriverFactory

@pytest.fixture(scope="function")
def driver():
    wd_factory = WebDriverFactory()
    driver_instance = wd_factory.get_driver()
    driver_instance.maximize_window()
    yield driver_instance
    driver_instance.quit()
  1. Интеграция с CI/CD и отчетностью:
    • Параметризация запуска через pytest-маркеры (например, @pytest.mark.smoke).
    • Генерация отчетов Allure с кастомными шагами и аттачами (скриншоты при падении, логи).
    • Контейнеризация тестового окружения с помощью Docker и docker-compose.
    • Настройка джобы в Jenkins/GitLab CI, которая запускает тесты, генерирует отчет и отправляет уведомление в Slack.

Такой подход обеспечивает быстрый старт новых тестов, легкую поддержку и надежную интеграцию в процесс разработки.

Ответ 18+ 🔞

Ну, слушай, а вот ты решил с нуля фреймворк запилить. Ну, блядь, молодец, авантюрист. Главное тут — не нагородить такую хуйню, чтобы потом самому в ней через месяц разбираться, как в джунглях Амазонки с компасом, сделанным из хуя и палки.

Так, значит, три кита: модульность, чтобы всё по полочкам, поддерживаемость, чтобы не орать «ёбаный насос!» при каждом изменении, и впихнуть это всё в CI/CD, чтобы пайплайн не сдох на первом же тесте.

Вот, смотри, как обычно всё выглядит, если не быть распиздяем:

test_framework/
├── conftest.py              # Тут вся наша магия с фикстурами, наше всё
├── pytest.ini               # Чтоб pytest не тупил и знал, где ему искать
├── requirements.txt         # Священный список, без которого нихуя не запустится
├── config/                  # Тут мы храним все наши тайны и ключи
│   ├── __init__.py
│   ├── settings.py          # Читаем .env, тырим константы
│   └── environments.py      # Разбираемся, где мы: в дев, стейдже или уже в проде (страшно)
├── core/                    # Сердце, блядь, мотор
│   ├── __init__.py
│   ├── webdriver_factory.py # Один на всех браузер, чтоб не плодить сущности
│   └── api_client.py        # Обёртка над requests, чтоб не писать одно и то же
├── pages/                   # Page Object, священная корова автотестеров
│   ├── __init__.py
│   ├── base_page.py         # Родитель, от которого все пляшут
│   └── login_page.py        # Ну, логиниться же все любят
├── tests/                   # А вот и сами тесты, ради чего весь сыр-бор
│   ├── __init__.py
│   ├── ui/                  # Тыкаем в кнопочки
│   └── api/                 # Дёргаем ручки
└── utils/                   # Всякая полезная хуйня, которая везде нужна
    ├── data_generators.py   # Генератор имён «qwerty123»
    └── allure_helpers.py    # Чтоб в Allure отчёт был красивый, а не как у свиньи

А теперь, блядь, самое интересное — как это всё оживает.

1. Браузер. Один на всех, чтоб не дрались. Вот смотри, делаем фабрику, да ещё и синглтон, чтобы не плодить двадцать окон браузера, а то комп взвоет, как сука.

# core/webdriver_factory.py
from selenium.webdriver import Chrome, Firefox
from selenium.webdriver.chrome.options import Options as ChromeOptions

class WebDriverFactory:
    _instance = None  # Вот он, наш единственный и неповторимый

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def get_driver(self, browser_name="chrome"):
        if browser_name.lower() == "chrome":
            options = ChromeOptions()
            options.add_argument("--headless=new")  # Для CI, чтоб безголово и быстро
            return Chrome(options=options)
        elif browser_name.lower() == "firefox":
            return Firefox()
        else:
            raise ValueError(f"Unsupported browser: {browser_name}")  # Иди нахуй с твоим Edge

2. Фикстура — кормилица наша. Пихаем это в conftest.py, чтоб каждый тест мог просто сказать «дай драйвер» и не париться.

import pytest
from core.webdriver_factory import WebDriverFactory

@pytest.fixture(scope="function")
def driver():
    wd_factory = WebDriverFactory()  # Берём нашу фабрику
    driver_instance = wd_factory.get_driver()  # Получаем браузер
    driver_instance.maximize_window()  # На весь экран, блядь!
    yield driver_instance  # Отдаём тесту, пусть работает
    driver_instance.quit()  # Всё, приехали, закрываем лавочку

3. А теперь, сука, CI/CD и красота в отчётах. Тут уже начинается магия по-крупному.

  • Маркеры в pytest: Вешаем @pytest.mark.smoke на самые важные тесты и гоняем только их, когда времени в обрез. Удобно, ёпта.
  • Allure: Настраиваем так, чтобы при падении теста автоматом летел скриншот и логи. Чтоб не гадать, «а что там сломалось?», а сразу видеть — «ах, вот эта кнопка, пидарас, не нажимается».
  • Docker: Засовываем всё это в контейнер, чтобы на любой машине запускалось одинаково. Никаких «а у меня работает».
  • Jenkins/GitLab CI: Настраиваем джобу, которая: выкачивает код, ставит зависимости, запускает тесты, генерирует красивый Allure-отчёт и, если всё сломалось, орёт в Slack. Автоматизация, блядь, мать её.

Вот такой, сука, подход. Получается система, где новый тест написать — раз плюнуть, поддерживать — не больно, а в пайплайн встроить — как два пальца. Главное — начать не с хаоса, а с нормальной структуры, а то потом волосы на жопе рвать будешь.