e2e_Pets — End-to-End тесты портала учёта животных
Автоматизированное тестирование портала vet.goznak.ru
на базе Playwright + pytest.
Содержание
- Структура проекта
- Быстрый старт
- Запуск тестов
- Поддержка Стандартного и ГОСТ TLS
- Создание нового теста
- Добавление генератора данных (ФЛК)
- Добавление статического артефакта
- Создание теста через визуальный рекордер (Codegen)
Структура проекта
e2e_Pets/
├── .env # Учётные данные и настройки среды (не в git)
├── .env.example # Шаблон .env
├── conftest.py # Корневой conftest (CLI-флаги, HTML-отчёт и метаданные)
├── pytest.ini # Конфигурация pytest
├── requirements.txt # Зависимости
│
└── tests/
├── conftest.py # Глобальные фикстуры (browser_page, bp_state, _auto_browser)
├── testdata/
│ └── generators.py # Генераторы случайных данных по ФЛК
├── utils/
│ ├── tls.py # Детектор TLS и адаптивный менеджер браузеров
│ └── test_tls.py # Модульные тесты механизмов TLS
│
├── 01_vetdept/ # ЛК: Управление ветеринарии
│ ├── conftest.py # fixtures_dir, shared_state
│ ├── fixtures/ # Статические артефакты (фото, PDF)
│ └── BP01_navigation/ # Бизнес-процесс 01
│ └── test_bp01_navigation.py
│
└── 02_vetstation/ # ЛК: Ветеринарная станция
├── conftest.py
├── fixtures/
└── BP01_dashboard/ # Бизнес-процесс 01
└── test_bp01_dashboard.py
Иерархия тестов: Проект → ЛК (роль) → БП (BP01_...) → Шаги (test_s01_..., test_s02_...)
Быстрый старт
1. Клонировать репозиторий
git clone <url>
cd e2e_Pets
2. Создать виртуальное окружение и установить зависимости
python -m venv venv
.\venv\Scripts\pip.exe install -r requirements.txt
.\venv\Scripts\playwright.exe install chromium
3. Настроить конфигурацию и учётные данные
Copy-Item .env.example .env
# Открыть .env и указать BASE_URL, логины и пароли ролей
4. (Опционально) Настройка ГОСТ-браузера
Если целевой портал защищён ГОСТ TLS (ГОСТ Р 34.12-2015, 34.10-2012) или вы хотите тестировать ГОСТ-контур:
- Скачайте архив Chromium-GOST (например,
chromium-gost-...-windows-amd64.zip). - Распакуйте его в стандартный каталог
%LOCALAPPDATA%\Chromium-Gost(илиC:\Program Files\Chromium-Gost). Механизм тестов обнаружит его автоматически. - Альтернатива: если браузер распакован в произвольную папку, укажите путь в
.env:CHROMIUM_GOST_PATH=C:\MyPrograms\chromium-gost\chrome.exe - Убедитесь, что на компьютере установлен криптопровайдер с ГОСТ-алгоритмами (КриптоПро CSP, Security Code CSP / Континент-АП).
Запуск тестов
Все команды выполняются из корня проекта (
e2e_Pets/).
HTML-отчёт со статусом, типом TLS и запущенным браузером автоматически формируется вhtml-report/report.html.
Автоматический запуск (рекомендуемый)
По умолчанию включён режим --tls-mode=auto. Система перед стартом проверяет сокет целевого BASE_URL:
- Если сайт доступен по стандартному TLS → автоматически запускается встроенный Chromium Playwright.
- Если сайт защищён ГОСТ TLS → автоматически запускается Chromium-GOST.
# Запуск всех тестов в авто-режиме
.\venv\Scripts\pytest.exe
Принудительный выбор режима TLS
Если вы хотите явно зафиксировать браузер в обход автоопределения:
# Принудительно запустить через Chromium-GOST
.\venv\Scripts\pytest.exe --tls-mode=gost
# Принудительно запустить через стандартный Chromium Playwright
.\venv\Scripts\pytest.exe --tls-mode=standard
Запуск тестов конкретного ЛК (роли)
# ЛК Управление ветеринарии (все бизнес-процессы)
.\venv\Scripts\pytest.exe tests/01_vetdept/
# ЛК Ветеринарная станция (все бизнес-процессы)
.\venv\Scripts\pytest.exe tests/02_vetstation/
Запуск конкретного бизнес-процесса или шага
# Запуск BP01 в ЛК Управление ветеринарии
.\venv\Scripts\pytest.exe tests/01_vetdept/BP01_navigation/
# Запуск только шага 1 (Dashboard)
.\venv\Scripts\pytest.exe tests/01_vetdept/BP01_navigation/ -k "s01"
# Запуск шагов 1 и 2
.\venv\Scripts\pytest.exe tests/01_vetdept/BP01_navigation/ -k "s01 or s02"
Полезные флаги запуска
| Флаг | Описание |
|---|---|
-v |
Подробный вывод с отображением docstring каждого шага |
--tls-mode=auto |
Режим TLS: auto (по умолчанию), gost или standard |
--ignore-https-errors |
Игнорировать ошибки сертификатов SSL (по умолчанию включено) |
-k "BP01" |
Фильтр тестов по подстроке в имени класса или метода |
--co -q |
Только показать список тестов без их выполнения |
--slowmo 500 |
Замедление действий в браузере (в мс, по умолчанию 1000мс в pytest.ini) |
-o addopts="" |
Сбросить дефолтные флаги pytest.ini (например, для headless-запуска в CI) |
# Просмотр дерева тестов без запуска браузера
.\venv\Scripts\pytest.exe --co -q
# Быстрый запуск без задержки slowmo
.\venv\Scripts\pytest.exe tests/01_vetdept/ --slowmo 0
# Запуск в режиме ГОСТ с подробным выводом
.\venv\Scripts\pytest.exe tests/01_vetdept/ --tls-mode=gost -v
Поддержка Стандартного и ГОСТ TLS
В проект встроен универсальный механизм запуска, который автоматически определяет, какой протокол шифрования используется на целевом портале (BASE_URL), и выбирает подходящий браузер.
Как работает автоопределение:
-
Двухфазная детекция:
- Выполняется тестовое SSL-рукопожатие сокета.
- Если сайт использует стандартные алгоритмы (TLS 1.2 / TLS 1.3, AES, CHACHA20 и др.), даже при наличии самоподписанного или тестового корпоративного сертификата, система распознаёт его как Стандартный TLS и запускает встроенный Chromium Playwright.
- Если сервер требует российские криптографические алгоритмы ГОСТ (ГОСТ Р 34.12-2015 «Кузнечик»/«Магма», ГОСТ Р 34.10-2012, 28147-89), которые не поддерживаются штатным OpenSSL в Python, система идентифицирует ГОСТ TLS.
-
Автопоиск браузера для ГОСТ:
- Система автоматически сканирует стандартные каталоги установки:
%LOCALAPPDATA%\Chromium-Gost\Application\chrome.exe%PROGRAMFILES%\Chromium-Gost\chrome.exe(и вProgram Files (x86))- Яндекс.Браузер (при установленном КриптоПро CSP):
%LOCALAPPDATA%\Yandex\...,%PROGRAMFILES%\Yandex\... - Системный
PATH(chromium-gost,yandex-browser).
- Если браузер найден в системе, тесты стартуют автоматически без необходимости вручную прописывать путь в
.env.
- Система автоматически сканирует стандартные каталоги установки:
-
Варианты запуска под ГОСТ:
- Автозапуск (рекомендуется): Установить Chromium-GOST или Яндекс.Браузер с КриптоПро CSP. При нестандартном расположении указать в
.env:CHROMIUM_GOST_PATH=C:\CustomPath\chrome.exe - Подключение по CDP (если браузер уже запущен вручную):
Тесты автоматически подключатся к открытому браузеру на порту
chrome.exe --remote-debugging-port=9222 --no-first-run9222.
- Автозапуск (рекомендуется): Установить Chromium-GOST или Яндекс.Браузер с КриптоПро CSP. При нестандартном расположении указать в
-
Управление режимом TLS:
- Через аргумент CLI:
.\venv\Scripts\pytest.exe --tls-mode=auto # Автоопределение (по умолчанию) .\venv\Scripts\pytest.exe --tls-mode=gost # Принудительно ГОСТ .\venv\Scripts\pytest.exe --tls-mode=standard # Принудительно Стандартный - Либо через
.env:TLS_MODE=auto # auto | gost | standard
- Через аргумент CLI:
-
Отображение в HTML-отчёте:
- В блоке Environment сгенерированного отчёта
html-report/report.htmlфиксируются:- Реальный режим TLS и использованный шифр (например,
Стандартный TLS (TLSv1.3, TLS_AES_256_GCM_SHA384)). - Тип и путь фактически запущенного браузера (
Chromium (Playwright)/Chromium GOST/Яндекс.Браузер).
- Реальный режим TLS и использованный шифр (например,
- В блоке Environment сгенерированного отчёта
Создание нового теста
Нужен новый бизнес-процесс?
Опиши его в свободной форме — например:
«Нужен тест для ветстанции: открыть список животных, применить фильтр по кошкам, убедиться что список отфильтровался, открыть карточку первого животного, проверить наличие кнопки редактирования»
ИИ-ассистент создаст файл в нужном месте, следуя архитектуре проекта.
Если хочешь создать вручную — шаблон:
1. Создать папку бизнес-процесса
# Для роли vetstation, новый бизнес-процесс BP02
mkdir tests\02_vetstation\BP02_animals
New-Item tests\02_vetstation\BP02_animals\__init__.py
2. Создать файл теста
Имя файла: test_bp02_<название>.py
import os
import pytest
from playwright.sync_api import Page, expect
BASE_URL = os.getenv("BASE_URL", "https://vet.goznak.ru")
@pytest.mark.vetstation # <- маркер роли
@pytest.mark.usefixtures("browser_page") # <- авторизация и браузер
class TestBP02Animals:
"""
BP02: Краткое описание бизнес-процесса
Проверяет что пользователь может:
- Шаг 1: ...
- Шаг 2: ...
"""
@pytest.mark.dependency(name="BP02::s01")
def test_s01_<название>(self, browser_page: Page, bp_state: dict):
"""Шаг 1: Описание шага — именно это появится в отчёте"""
# ... код теста ...
bp_state["key"] = "value" # передать данные следующему шагу
@pytest.mark.dependency(name="BP02::s02", depends=["BP02::s01"])
def test_s02_<название>(self, browser_page: Page, bp_state: dict):
"""Шаг 2: Описание шага"""
value = bp_state["key"] # получить данные от предыдущего шага
# ... код теста ...
3. Правила именования
| Элемент | Формат | Пример |
|---|---|---|
| Папка БП | BP<NN>_<описание> |
BP02_animals |
| Файл теста | test_bp<NN>_<описание>.py |
test_bp02_animals.py |
| Класс | TestBP<NN><Описание> |
TestBP02Animals |
| Метод (шаг) | test_s<NN>_<описание> |
test_s01_open_list |
| Зависимость | BP<NN>::s<NN> |
BP02::s01 |
4. Маркеры ролей
| Маркер | Роль |
|---|---|
@pytest.mark.vetdept |
Управление ветеринарии |
@pytest.mark.vetstation |
Ветеринарная станция |
@pytest.mark.prefecture |
Администрация (префектура) |
Добавление генератора данных (ФЛК)
Файл: tests/testdata/generators.py
Содержит пошаговое руководство по добавлению нового генератора прямо в начале файла. Коротко:
# Шаблон новой функции-генератора:
def gen_<сущность>_<поле>() -> str:
"""
Краткое описание.
ФЛК:
- Правило 1
- Правило 2
Пример вывода: 'AB1234'
"""
# ... реализация ...
Использование в тесте:
from tests.testdata.generators import gen_microchip, gen_animal_name
def test_s01_fill_form(self, browser_page, bp_state):
name = gen_animal_name()
chip = gen_microchip()
browser_page.fill("#name", name)
browser_page.fill("#chip", chip)
bp_state["name"] = name # передаём следующему шагу
Добавление статического артефакта
-
Положи файл в папку
fixtures/нужной роли:tests/01_vetdept/fixtures/images/cat_photo.jpg -
Раскомментируй шаблон фикстуры в
tests/<роль>/conftest.py:@pytest.fixture(scope="session") def cat_photo(fixtures_dir) -> Path: """Фото кошки для теста загрузки.""" return fixtures_dir / "images" / "cat_photo.jpg" -
Используй в тесте:
def test_s02_upload(self, browser_page, bp_state, cat_photo): browser_page.set_input_files("input[type=file]", str(cat_photo))
Создание теста через визуальный рекордер (Codegen)
Playwright Codegen — встроенный инструмент записи действий в браузере. Ты кликаешь, вводишь текст и переходишь по страницам — Codegen генерирует Python-код, который воспроизводит твои действия.
Запуск рекордера
Вариант 1: Универсальный запуск с поддержкой ГОСТ TLS (рекомендуемый)
Штатная команда playwright codegen не умеет работать со сторонними браузерами напрямую. Для записи тестов на сайтах с ГОСТ TLS (а также обычным TLS) используйте встроенный скрипт codegen.py:
# Запуск для URL по умолчанию из .env (с автоопределением TLS и выбором браузера):
.\venv\Scripts\python.exe codegen.py
# Принудительный запуск в режиме ГОСТ TLS (через Chromium-GOST):
.\venv\Scripts\python.exe codegen.py --gost
# Запуск для произвольного адреса:
.\venv\Scripts\python.exe codegen.py https://vet.goznak.ru
Вариант 2: Штатный запуск (только для сайтов со стандартным TLS)
# Открыть встроенный Chromium с рекордером
.\venv\Scripts\playwright.exe codegen https://vet.goznak.ru
# Открыть рекордер и сразу сохранить результат в файл
.\venv\Scripts\playwright.exe codegen https://vet.goznak.ru --output tests\codegen_draft.py
При запуске откроются два окна:
- Браузер (Chromium-GOST или Playwright Chromium) — кликайте, заполняйте формы и переходите по разделам.
- Playwright Inspector — окно записи, в котором в реальном времени формируется Python-код.
Порядок работы
- Запусти рекордер командой выше
- Авторизуйся под нужной ролью (введи логин/пароль из
.env) - Выполни нужный бизнес-процесс — все действия записываются автоматически
- Скопируй сгенерированный код из Playwright Inspector
- Адаптируй код под архитектуру проекта (см. ниже)
Как адаптировать код из Codegen в архитектуру проекта
Codegen генерирует плоский скрипт, проект использует класс с шагами.
Ниже показано как преобразовать одно в другое.
До (вывод Codegen):
from playwright.sync_api import Playwright, sync_playwright, expect
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
context = browser.new_context()
page = context.new_page()
page.goto("https://vet.goznak.ru/auth")
page.get_by_role("textbox", name="Email").fill("gz_vetstation@vet.goznak.ru")
page.get_by_role("textbox", name="Введите пароль").fill("A6wrpzpD5T")
page.get_by_role("button", name="Войти в аккаунт").click()
page.get_by_role("link", name="Животные").click()
expect(page).to_have_url("https://vet.goznak.ru/animals")
page.get_by_role("button", name="Добавить животное").click()
page.get_by_label("Кличка").fill("Мурка")
page.get_by_role("button", name="Сохранить").click()
expect(page.get_by_text("Мурка")).to_be_visible()
context.close()
browser.close()
with sync_playwright() as playwright:
run(playwright)
После (архитектура проекта):
import os
import pytest
from playwright.sync_api import Page, expect
BASE_URL = os.getenv("BASE_URL", "https://vet.goznak.ru")
@pytest.mark.vetstation
@pytest.mark.usefixtures("browser_page")
class TestBP02AddAnimal:
"""
BP02: Добавление нового животного
Проверяет что пользователь может:
- Перейти в раздел Животные
- Открыть форму добавления
- Заполнить кличку и сохранить
- Убедиться что животное появилось в списке
"""
# Авторизация и логин/пароль — убраны полностью:
# этим занимается фикстура browser_page (см. tests/conftest.py)
@pytest.mark.dependency(name="BP02::s01")
def test_s01_go_to_animals(self, browser_page: Page, bp_state: dict):
"""Шаг 1: Перейти в раздел Животные"""
browser_page.get_by_role("link", name="Животные").click()
expect(browser_page).to_have_url(BASE_URL + "/animals", timeout=10000)
@pytest.mark.dependency(name="BP02::s02", depends=["BP02::s01"])
def test_s02_open_add_form(self, browser_page: Page, bp_state: dict):
"""Шаг 2: Открыть форму добавления животного"""
browser_page.get_by_role("button", name="Добавить животное").click()
expect(browser_page.get_by_label("Кличка")).to_be_visible(timeout=10000)
@pytest.mark.dependency(name="BP02::s03", depends=["BP02::s02"])
def test_s03_fill_and_save(self, browser_page: Page, bp_state: dict):
"""Шаг 3: Заполнить кличку и сохранить"""
from tests.testdata.generators import gen_animal_name
name = gen_animal_name() # случайное имя по ФЛК
browser_page.get_by_label("Кличка").fill(name)
browser_page.get_by_role("button", name="Сохранить").click()
bp_state["animal_name"] = name # передаём шагу 4
@pytest.mark.dependency(name="BP02::s04", depends=["BP02::s03"])
def test_s04_verify_in_list(self, browser_page: Page, bp_state: dict):
"""Шаг 4: Проверить что животное появилось в списке"""
expect(
browser_page.get_by_text(bp_state["animal_name"])
).to_be_visible(timeout=10000)
Что изменилось при адаптации
| Было в Codegen | Стало в проекте |
|---|---|
browser.launch() + context.new_page() |
Фикстура browser_page (авто) |
| Явный логин/пароль в коде | Берётся из .env через фикстуру |
Один плоский def run() |
Класс с методами-шагами |
| Нет зависимостей между шагами | @pytest.mark.dependency(depends=[...]) |
Хардкод данных ("Мурка") |
Генераторы ФЛК (gen_animal_name()) |
| Нет передачи данных между шагами | bp_state["key"] = value |
| Нет отображения в отчёте | Docstring шага → колонка Test в отчёте |