Небольшое введение в автотестирование бизнес-процессов Гриндаты, с чего начать и как продолжить

отредактировано 28 окт Раздел: Прочие темы

Небольшое введение в автотестирование бизнес-процессов Гриндаты, с чего начать и как продолжить
Python, pytest, patchright

У нас довольно сложные бизнес-процессы, с множеством исполнителей, и ручное тестирование сильно затягивает вывод реализованных БП в прод

Появились некоторые наработки по автотестам, которые, к сожалению, с большой вероятностью придётся скоро убрать в стол 😐
В целом, вся информация легко находится в интернетах и gpt отлично в этом помогает, но решил оставить её здесь для коллег, это поможет быстро начать писать автотесты

Используется классический подход к автотестам, PageObject и иже с ними. Стек выбран самый популярный, и достаточно лёгкий для входа. В статье не рассматривается установка питона и пр. Предполагается, что читающий знаком с питоном, виртуальными окружениями venv, системой управления зависимостями pip

В конце статьи мы получим полностью работающий тест авторизации (1) в Гриндату несколькими пользователями разных ролей (Групп доступа), и отчёт о таком тестировании. Тесты предполагают, что пользователи уже созданы
(1) На самом деле - это тесты аутентификации, они проверяют только корректность логина/пароля; определение прав пользователя это следующий шаг, в статье он не рассматривается. Мы лишь проверим, что пользователь после авторизации попадает на свою стартовую страницу

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

gd-py-autotests/
├── tests/
│   ├── test_auth.py           # Тесты авторизации
│   └── conftest.py            # Конфигурация pytest
├── pages/
│   ├── base_page.py           # Базовый класс для всех страниц
│   └── login_page.py          # Страница авторизации
├── requirements.txt
├── pytest.ini
└── test_data.yaml             # Тестовые данные, пользователи

Создание и активация виртуального окружения venv

python -m venv .venv
.venv\Scripts\activate     # Windows
. .venv/bin/activate       # Linux

Установка pytest, patchright

# можно установить последние версии pytest и patchright, 
# и их зависимости следующей командой:
pip install pytest patchright pyyaml

# а можно установить актуальные на момент статьи версии 
# всех зависимостей из файла requirements.txt (его содержимое ниже):
pip install -r requirements.txt

requirements.txt

# requirements.txt
colorama==0.4.6
greenlet==3.2.4
iniconfig==2.1.0
Jinja2==3.1.6
MarkupSafe==3.0.3
packaging==25.0
patchright==1.55.2
pluggy==1.6.0
pyee==13.0.0
Pygments==2.19.2
pytest==8.4.2
pytest-html==4.1.1
pytest-metadata==3.1.1
PyYAML==6.0.3
typing_extensions==4.15.0

Установка браузеров patchright

patchright install

Эта команда установит основные браузеры (firefox, chtomium) в виртуальное окружение venv
Проверить установку браузеров можно командой

patchright cr https://ya.ru # откроет страницу Яндекс-поиска в chronium
patchright ff https://ya.ru # в firefox

Тестовые данные
В тесте авторизации будут использованы несколько пользователей, которые должны быть созданы в Гриндате; и один пользователь, которого не существует (проверим, что Гриндата не авторизует кого попало). Опишем этих пользователей в файле test_data.yaml

# test_data.yaml
users:
  - role: "Менеджер отдела авторизации"
    username: "manager"
    password: "password"
    expected_url_part: "card/666000" # Ожидаем, что попадём на эту страницу после логина

  - role: "Руководитель отдела авторизации"
    username: "director"
    password: "password"
    expected_url_part: "card/666001"

  - role: "Специалист отдела авторизации"
    username: "specialist"
    password: "password"
    expected_url_part: "card/666002"

  - role: "Невалидный пользователь"
    username: "invalid_user"
    password: "wrong_password"
    expected_url_part: "login" # Ожидаем, что останемся на странице логина

Параметры, конфигурация pytest, patchright
Используем только chromium; параметром slow_mo немного замедляем прохождение тестов (если поставить 1000 (1 секунда), это заметно замедлит выполнение тестов, увеличит созерцаемость, повысит осознанность)

# conftest.py
import pytest
from patchright.sync_api import sync_playwright

@pytest.fixture(scope="session")
def browser():
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=False,
            channel="chrome",
            slow_mo=100,
        )
        yield browser
        browser.close()

@pytest.fixture(scope="function")
def page(browser):
    page = browser.new_page()
    yield page
    page.close()

PageObject для страницы авторизации
Пара файлов: pages/base_page.py, pages/login_page.py

# base_page.py, абстрактный класс BaseObject, родитель всех страниц
from patchright.sync_api import Page

class BasePage:
    def __init__(self, page: Page):
        self.page = page

    def goto(self, url: str):
        self.page.goto(url)
        self.page.wait_for_load_state('networkidle')

    def is_element_visible(self, selector: str) -> bool:
        return self.page.is_visible(selector)

# login_page.py, класс LoginPage, описывает страницу авторизации Гриндаты
from pages.base_page import BasePage

class LoginPage(BasePage):
    def __init__(self, page):
        super().__init__(page)
        self.username_input = 'input[class="login-input"]'
        self.password_input = 'input[class="password-input"]'
        self.login_button = 'button:has-text("Войти")' # Предполагается, что используется интерфейс на русском языке

    def login(self, username: str, password: str):
        self.page.fill(self.username_input, username)
        self.page.fill(self.password_input, password)
        self.page.click(self.login_button)

    def is_login_form_visible(self) -> bool:
        return self.is_element_visible(self.username_input)

Тест авторизации
Файл tests/test_auth.py

# test_auth.py
import pytest
import yaml
from pages.login_page import LoginPage

def load_user_data_from_yaml():
    """Читает пользователей из YAML-файла"""
    with open("test_data.yaml", "r", encoding="utf-8") as f:
        data = yaml.safe_load(f)
    # Создаем "красивые" ID для отображения в отчете pytest
    return [pytest.param(user, id=user.get('role', 'unknown_role')) for user in data['users']]

class TestAuthentication:
    @pytest.mark.parametrize("user_data", load_user_data_from_yaml())
    def test_user_login(self, page, user_data):
        """
        Тест проверяет авторизацию для пользователей c разными ролями (группами доступа)
        """
        login_page = LoginPage(page)
        login_page.goto("https://test-ecocultura.local/")
        assert login_page.is_login_form_visible()
        login_page.login(user_data["username"], user_data["password"])
        page.wait_for_timeout(5000) # такие таймауты мы называем злом, но статья не об этом
                                    # https://trofimovdigital.ru/blog/jedi-principles-for-write-ideal-autotests
        assert user_data["expected_url_part"] in page.url

That's all folks

# Запуск всех тестов
pytest

# Запуск с подробностями
pytest -v -s

# Запуск конкретного теста
pytest tests/test_auth.py::TestAuthentication::test_user_login

# Запуск тестов, генерация отчёта в формате HTML
pytest --html=report.html --self-contained-html

# Запуск тестов, генерация отчёта в формате JUnit
pytest --junitxml="report.xml"
Войдите или Зарегистрируйтесь чтобы комментировать.