Описан workflow визуальной записи тестов: - Команды запуска codegen (с сохранением в файл и без) - Порядок работы: запуск → авторизация → запись → адаптация - Полный пример: сырой вывод Codegen → класс с шагами - Таблица преобразований: что убирается и что добавляется
418 lines
16 KiB
Markdown
418 lines
16 KiB
Markdown
# e2e_Pets — End-to-End тесты портала учёта животных
|
||
|
||
Автоматизированное тестирование портала [vet.goznak.ru](https://vet.goznak.ru)
|
||
на базе **Playwright + pytest**.
|
||
|
||
---
|
||
|
||
## Содержание
|
||
|
||
- [Структура проекта](#структура-проекта)
|
||
- [Быстрый старт](#быстрый-старт)
|
||
- [Запуск тестов](#запуск-тестов)
|
||
- [Создание нового теста](#создание-нового-теста)
|
||
- [Добавление генератора данных (ФЛК)](#добавление-генератора-данных-флк)
|
||
- [Добавление статического артефакта](#добавление-статического-артефакта)
|
||
|
||
---
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
e2e_Pets/
|
||
├── .env # Учётные данные (не в git)
|
||
├── .env.example # Шаблон .env
|
||
├── pytest.ini # Конфигурация pytest
|
||
├── requirements.txt # Зависимости
|
||
│
|
||
└── tests/
|
||
├── conftest.py # Глобальные фикстуры (browser_page, bp_state)
|
||
├── testdata/
|
||
│ └── generators.py # Генераторы случайных данных по ФЛК
|
||
│
|
||
├── 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. Клонировать репозиторий
|
||
|
||
```powershell
|
||
git clone <url>
|
||
cd e2e_Pets
|
||
```
|
||
|
||
### 2. Создать виртуальное окружение и установить зависимости
|
||
|
||
```powershell
|
||
python -m venv venv
|
||
.\venv\Scripts\pip.exe install -r requirements.txt
|
||
.\venv\Scripts\playwright.exe install chromium
|
||
```
|
||
|
||
### 3. Настроить учётные данные
|
||
|
||
```powershell
|
||
Copy-Item .env.example .env
|
||
# Открыть .env и заполнить логины/пароли
|
||
```
|
||
|
||
---
|
||
|
||
## Запуск тестов
|
||
|
||
> Все команды выполняются из корня проекта (`e2e_Pets/`).
|
||
> HTML-отчёт автоматически создаётся в `html-report/report.html`.
|
||
|
||
---
|
||
|
||
### Запустить ВСЕ тесты
|
||
|
||
```powershell
|
||
.\venv\Scripts\pytest.exe
|
||
```
|
||
|
||
---
|
||
|
||
### Запустить тесты конкретного ЛК (роли)
|
||
|
||
```powershell
|
||
# ЛК Управление ветеринарии
|
||
.\venv\Scripts\pytest.exe tests/01_vetdept/
|
||
|
||
# ЛК Ветеринарная станция
|
||
.\venv\Scripts\pytest.exe tests/02_vetstation/
|
||
```
|
||
|
||
---
|
||
|
||
### Запустить конкретный бизнес-процесс
|
||
|
||
```powershell
|
||
# BP01 навигации в ЛК Управление ветеринарии
|
||
.\venv\Scripts\pytest.exe tests/01_vetdept/BP01_navigation/
|
||
|
||
# BP01 дашборда в ЛК Ветеринарная станция
|
||
.\venv\Scripts\pytest.exe tests/02_vetstation/BP01_dashboard/
|
||
```
|
||
|
||
---
|
||
|
||
### Запустить конкретный шаг
|
||
|
||
```powershell
|
||
# Только шаг 3 из BP01 ветстанции
|
||
.\venv\Scripts\pytest.exe tests/02_vetstation/BP01_dashboard/ -k "s03"
|
||
|
||
# Только шаги 1 и 2
|
||
.\venv\Scripts\pytest.exe tests/02_vetstation/BP01_dashboard/ -k "s01 or s02"
|
||
```
|
||
|
||
---
|
||
|
||
### Полезные флаги
|
||
|
||
| Флаг | Описание |
|
||
|---|---|
|
||
| `-v` | Подробный вывод с именами шагов |
|
||
| `--headed` | Показывать браузер (по умолчанию включён в `pytest.ini`) |
|
||
| `--headless` | Запустить без браузера (быстрее) |
|
||
| `--slowmo 500` | Замедление действий на 500мс (для отладки) |
|
||
| `-k "BP01"` | Запустить только тесты содержащие "BP01" в имени |
|
||
| `--co -q` | Только показать список тестов без запуска |
|
||
|
||
```powershell
|
||
# Показать список всех тестов (без запуска)
|
||
.\venv\Scripts\pytest.exe --co -q
|
||
|
||
# Запустить без браузера и без замедления
|
||
.\venv\Scripts\pytest.exe --headless --slowmo 0
|
||
|
||
# Запустить в headless-режиме только vetdept
|
||
.\venv\Scripts\pytest.exe tests/01_vetdept/ --headless --slowmo 0
|
||
```
|
||
|
||
---
|
||
|
||
## Создание нового теста
|
||
|
||
### Нужен новый бизнес-процесс?
|
||
|
||
Опиши его в свободной форме — например:
|
||
|
||
> *«Нужен тест для ветстанции: открыть список животных, применить фильтр
|
||
> по кошкам, убедиться что список отфильтровался, открыть карточку
|
||
> первого животного, проверить наличие кнопки редактирования»*
|
||
|
||
ИИ-ассистент создаст файл в нужном месте, следуя архитектуре проекта.
|
||
|
||
---
|
||
|
||
### Если хочешь создать вручную — шаблон:
|
||
|
||
#### 1. Создать папку бизнес-процесса
|
||
|
||
```powershell
|
||
# Для роли vetstation, новый бизнес-процесс BP02
|
||
mkdir tests\02_vetstation\BP02_animals
|
||
New-Item tests\02_vetstation\BP02_animals\__init__.py
|
||
```
|
||
|
||
#### 2. Создать файл теста
|
||
|
||
Имя файла: `test_bp02_<название>.py`
|
||
|
||
```python
|
||
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`](tests/testdata/generators.py)
|
||
|
||
Содержит пошаговое руководство по добавлению нового генератора прямо в начале файла. Коротко:
|
||
|
||
```python
|
||
# Шаблон новой функции-генератора:
|
||
|
||
def gen_<сущность>_<поле>() -> str:
|
||
"""
|
||
Краткое описание.
|
||
|
||
ФЛК:
|
||
- Правило 1
|
||
- Правило 2
|
||
|
||
Пример вывода: 'AB1234'
|
||
"""
|
||
# ... реализация ...
|
||
```
|
||
|
||
Использование в тесте:
|
||
|
||
```python
|
||
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`:
|
||
```python
|
||
@pytest.fixture(scope="session")
|
||
def cat_photo(fixtures_dir) -> Path:
|
||
"""Фото кошки для теста загрузки."""
|
||
return fixtures_dir / "images" / "cat_photo.jpg"
|
||
```
|
||
|
||
3. Используй в тесте:
|
||
```python
|
||
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-код, который воспроизводит твои действия.
|
||
|
||
### Запуск рекордера
|
||
|
||
```powershell
|
||
# Открыть браузер с рекордером (без авторизации)
|
||
.\venv\Scripts\playwright.exe codegen https://vet.goznak.ru
|
||
|
||
# Открыть браузер с рекордером и сохранить результат в файл
|
||
.\venv\Scripts\playwright.exe codegen https://vet.goznak.ru --output tests\codegen_draft.py
|
||
```
|
||
|
||
Откроются два окна:
|
||
- **Браузер** — управляй им как обычно: кликай, заполняй формы, переходи по разделам
|
||
- **Playwright Inspector** — в реальном времени показывает сгенерированный код
|
||
|
||
### Порядок работы
|
||
|
||
1. **Запусти рекордер** командой выше
|
||
2. **Авторизуйся** под нужной ролью (введи логин/пароль из `.env`)
|
||
3. **Выполни нужный бизнес-процесс** — все действия записываются автоматически
|
||
4. **Скопируй сгенерированный код** из Playwright Inspector
|
||
5. **Адаптируй код** под архитектуру проекта (см. ниже)
|
||
|
||
---
|
||
|
||
### Как адаптировать код из Codegen в архитектуру проекта
|
||
|
||
Codegen генерирует **плоский скрипт**, проект использует **класс с шагами**.
|
||
Ниже показано как преобразовать одно в другое.
|
||
|
||
#### До (вывод Codegen):
|
||
|
||
```python
|
||
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)
|
||
```
|
||
|
||
#### После (архитектура проекта):
|
||
|
||
```python
|
||
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 в отчёте |
|