Ответ
При создании фреймворка с нуля я фокусируюсь на модульности, поддерживаемости и интеграции в 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
Ключевые компоненты и их реализация:
- Управление браузером (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()
- Интеграция с 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. Автоматизация, блядь, мать её.
Вот такой, сука, подход. Получается система, где новый тест написать — раз плюнуть, поддерживать — не больно, а в пайплайн встроить — как два пальца. Главное — начать не с хаоса, а с нормальной структуры, а то потом волосы на жопе рвать будешь.