e2e_Pets — End-to-End тесты портала учёта животных

Автоматизированное тестирование портала vet.goznak.ru
на базе Playwright + pytest.


Содержание


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

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) или вы хотите тестировать ГОСТ-контур:

  1. Скачайте архив Chromium-GOST (например, chromium-gost-...-windows-amd64.zip).
  2. Распакуйте его в стандартный каталог %LOCALAPPDATA%\Chromium-Gost (или C:\Program Files\Chromium-Gost). Механизм тестов обнаружит его автоматически.
  3. Альтернатива: если браузер распакован в произвольную папку, укажите путь в .env:
    CHROMIUM_GOST_PATH=C:\MyPrograms\chromium-gost\chrome.exe
    
  4. Убедитесь, что на компьютере установлен криптопровайдер с ГОСТ-алгоритмами (КриптоПро 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), и выбирает подходящий браузер.

Как работает автоопределение:

  1. Двухфазная детекция:

    • Выполняется тестовое SSL-рукопожатие сокета.
    • Если сайт использует стандартные алгоритмы (TLS 1.2 / TLS 1.3, AES, CHACHA20 и др.), даже при наличии самоподписанного или тестового корпоративного сертификата, система распознаёт его как Стандартный TLS и запускает встроенный Chromium Playwright.
    • Если сервер требует российские криптографические алгоритмы ГОСТ (ГОСТ Р 34.12-2015 «Кузнечик»/«Магма», ГОСТ Р 34.10-2012, 28147-89), которые не поддерживаются штатным OpenSSL в Python, система идентифицирует ГОСТ TLS.
  2. Автопоиск браузера для ГОСТ:

    • Система автоматически сканирует стандартные каталоги установки:
      • %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.
  3. Варианты запуска под ГОСТ:

    • Автозапуск (рекомендуется): Установить Chromium-GOST или Яндекс.Браузер с КриптоПро CSP. При нестандартном расположении указать в .env:
      CHROMIUM_GOST_PATH=C:\CustomPath\chrome.exe
      
    • Подключение по CDP (если браузер уже запущен вручную):
      chrome.exe --remote-debugging-port=9222 --no-first-run
      
      Тесты автоматически подключатся к открытому браузеру на порту 9222.
  4. Управление режимом 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
      
  5. Отображение в HTML-отчёте:

    • В блоке Environment сгенерированного отчёта html-report/report.html фиксируются:
      • Реальный режим TLS и использованный шифр (например, Стандартный TLS (TLSv1.3, TLS_AES_256_GCM_SHA384)).
      • Тип и путь фактически запущенного браузера (Chromium (Playwright) / Chromium GOST / Яндекс.Браузер).

Создание нового теста

Нужен новый бизнес-процесс?

Опиши его в свободной форме — например:

«Нужен тест для ветстанции: открыть список животных, применить фильтр по кошкам, убедиться что список отфильтровался, открыть карточку первого животного, проверить наличие кнопки редактирования»

ИИ-ассистент создаст файл в нужном месте, следуя архитектуре проекта.


Если хочешь создать вручную — шаблон:

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  # передаём следующему шагу

Добавление статического артефакта

  1. Положи файл в папку fixtures/ нужной роли:

    tests/01_vetdept/fixtures/images/cat_photo.jpg
    
  2. Раскомментируй шаблон фикстуры в tests/<роль>/conftest.py:

    @pytest.fixture(scope="session")
    def cat_photo(fixtures_dir) -> Path:
        """Фото кошки для теста загрузки."""
        return fixtures_dir / "images" / "cat_photo.jpg"
    
  3. Используй в тесте:

    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-код.

Порядок работы

  1. Запусти рекордер командой выше
  2. Авторизуйся под нужной ролью (введи логин/пароль из .env)
  3. Выполни нужный бизнес-процесс — все действия записываются автоматически
  4. Скопируй сгенерированный код из Playwright Inspector
  5. Адаптируй код под архитектуру проекта (см. ниже)

Как адаптировать код из 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 в отчёте
Description
No description provided
Readme MIT 131 KiB
Languages
Python 100%