💻 Примеры: Allure + API-тесты

← К оглавлению урока

⚡ Самый важный пример

# test_companies.py
import allure

@allure.epic("компании")
@allure.severity("blocker")
class CompanyTest:
    @allure.id("ITCH-1")
    @allure.story("Получение списка компаний")
    @allure.feature("READ")
    @allure.title("Получение полного списка организаций")
    def test_get_companies(self):
        with allure.step("Получить список через API"):
            api_result = api.get_company_list()
        with allure.step("Получить список из БД"):
            db_result = db.get_companies()
        with allure.step("Сравнить размеры"):
            assert len(api_result) == len(db_result)

Пример 1: разметка тестов декораторами (из лекции)

Из лекции: @allure.epic и @allure.severity — общие для всего класса, выносим на уровень класса.

# test_x_clients_db.py
import allure
from CompanyApi import CompanyApi
from CompanyTable import CompanyTable

@allure.epic("компании")
@allure.severity("blocker")
class CompanyTest:
    api = CompanyApi("https://x-clients-be.onrender.com")
    db = CompanyTable("postgresql://user:password@host/db")

    @allure.id("ITCH-1")
    @allure.story("Получение списка компаний")
    @allure.feature("READ")
    @allure.title("Получение полного списка организаций")
    def test_get_companies(self):
        ...

    @allure.id("ITCH-2")
    @allure.story("Получение списка компаний")
    @allure.feature("READ")
    @allure.title("Получение списка активных организаций")
    @allure.description("Запрос организаций с параметром active = true")
    def test_get_active_companies(self):
        ...

    @allure.id("ITCH-3")
    @allure.story("Создание компаний")
    @allure.feature("CREATE")
    @allure.title("Создание организации")
    def test_add_new(self):
        ...

Пример 2: шаги в тесте (из лекции)

Из лекции: тест получения компаний, разбитый на три шага — каждый шаг виден в отчёте Allure в разделе «Test body».

# test_x_clients_db.py
import allure

@allure.id("ITCH-1")
@allure.story("Получение компаний")
@allure.feature("READ")
@allure.epic("компании")
def test_get_companies():
    with allure.step("Получить список компаний через API"):
        api_result = api.get_company_list()
    with allure.step("Получить список компаний из БД"):
        db_result = db.get_companies()
    with allure.step("Сравнить размеры двух списков"):
        assert len(api_result) == len(db_result)

Пример 3: вложенные шаги (из лекции)

Из лекции: тест создания компании с вложенными шагами. Помогает точно видеть, на каком этапе возникла ошибка.

# test_x_clients_db.py
@allure.id("ITCH-3")
@allure.story("Создание компаний")
@allure.feature("CREATE")
@allure.title("Создание организации")
def test_add_new(self):
    with allure.step("Получить количество организаций ДО"):
        body = self.api.get_company_list()
        len_before = len(body)

    with allure.step("Создать организацию"):
        with allure.step("Сгенерировать название"):
            name = "Autotest"
            descr = "Descr"
        with allure.step("Вызвать API-метод для создания"):
            result = self.api.create_company(name, descr)
            new_id = result["id"]

    with allure.step("Проверить поля новой организации"):
        body = self.api.get_company_list()
        for company in body:
            if company["id"] == new_id:
                assert company["name"] == name
                assert company["description"] == descr

    with allure.step("Получить количество организаций ПОСЛЕ"):
        body = self.api.get_company_list()
        len_after = len(body)

    with allure.step("Проверить, что список ДО меньше ПОСЛЕ на 1"):
        assert len_after - len_before == 1

    with allure.step("Удалить из БД новую организацию"):
        self.db.delete(new_id)

Пример 4: @allure.step на методах класса CompanyApi (из лекции)

Из лекции: декорируем методы API-класса. В названии шага используем приставку «api.», параметры подставляются автоматически через {имя_аргумента}.

# company_api.py
import allure
import requests

class CompanyApi:
    def __init__(self, url):
        self.url = url

    @allure.step("api. Получить список компаний")
    def get_company_list(self, params_to_add=None):
        resp = requests.get(self.url + "/company", params=params_to_add)
        assert resp.status_code == 200
        return resp.json()

    @allure.step("api. Получить токен авторизации пользователя '{user}'")
    def get_token(self, user="raphael", password="cool-but-crude"):
        creds = {"user": user, "password": password}
        resp = requests.post(self.url + "/auth/login", json=creds)
        assert resp.status_code == 200
        return resp.json()["userToken"]

    @allure.step("api. Получить компанию по id '{id}'")
    def get_company(self, id):
        resp = requests.get(self.url + f"/company/{id}")
        assert resp.status_code == 200
        return resp.json()

    @allure.step("api. Создать компанию '{name}' ({description})")
    def create_company(self, name, description=""):
        company = {"name": name, "description": description}
        resp = requests.post(self.url + "/company", json=company)
        assert resp.status_code == 201
        return resp.json()

    @allure.step("api. Редактировать компанию '{new_id}': новое имя '{new_name}'")
    def edit(self, new_id, new_name, new_descr):
        resp = requests.patch(self.url + f"/company/{new_id}",
                              json={"name": new_name, "description": new_descr})
        assert resp.status_code == 200
        return resp.json()

    @allure.step("api. Удалить компанию '{id}'")
    def delete(self, id):
        resp = requests.delete(self.url + f"/company/{id}")
        assert resp.status_code == 200
        return resp.json()

    @allure.step("api. (Де)активировать компанию '{id}', состояние '{isActive}'")
    def set_active_status(self, id, isActive):
        resp = requests.patch(self.url + f"/company/{id}",
                              json={"isActive": isActive})
        assert resp.status_code == 200
        return resp.json()

Пример 5: @allure.step на методах класса CompanyTable с вложением SQL (из лекции)

Из лекции: приставка «БД.» в шагах позволяет сразу видеть в отчёте, что метод взаимодействует с базой данных.

# company_table.py
import allure
from sqlalchemy import create_engine
from sqlalchemy.sql import text

class CompanyTable:
    __scripts = {
        "select": "select * from company",
        "select only active": 'select * from company where "is_active" = true',
        "delete by id": text("delete from company where id = :id_to_delete"),
        "insert new": text("insert into company (name) values (:new_name)"),
    }

    def __init__(self, connection_string):
        self.__db = create_engine(connection_string).connect()

    @allure.step("БД. Запросить список организаций")
    def get_companies(self):
        query = self.__db.execute(self.__scripts["select"])
        allure.attach(
            str(query.context.cursor.query),
            "SQL-запрос на получение списка компаний",
            allure.attachment_type.TEXT
        )
        return query.fetchall()

    @allure.step("БД. Запросить организацию по {id}")
    def get_company_by_id(self, id):
        # {id} подставится автоматически
        ...

    @allure.step("БД. Удалить организацию по {id}")
    def delete(self, id):
        params = {"id_to_delete": id}
        query = self.__db.execute(self.__scripts["delete by id"], parameters=params)
        allure.attach(
            str(query.context.cursor.query),
            "SQL-запрос на удаление компании",
            allure.attachment_type.TEXT
        )

    @allure.step("БД. Создать организацию с названием {name}")
    def create(self, name):
        params = {"new_name": name}
        query = self.__db.execute(self.__scripts["insert new"], parameters=params)
        allure.attach(
            str(query.context.cursor.query),
            "SQL-запрос на создание компании",
            allure.attachment_type.TEXT
        )

    @allure.step("БД. Получить максимальный id организации")
    def get_max_id(self):
        query = self.__db.execute("select max(id) from company")
        allure.attach(
            str(query.context.cursor.query),
            "SQL-запрос на получение максимального id",
            allure.attachment_type.TEXT
        )
        return query.fetchone()[0]

Пример 6: run.sh — автоматизация запуска (из лекции)

Из лекции: bash-скрипт для автоматического запуска тестов и генерации отчёта.

# run.sh
#!/bin/bash
results="allure-results"
report="allure-report"

# Очистить предыдущие результаты и отчёты
rm -rf $results
rm -rf $report

# Запустить тесты с генерацией данных для отчёта Allure
pytest --alluredir=$results

# Сгенерировать HTML-отчёт Allure
allure generate $results -o $report --clean

# Открыть отчёт в браузере
allure open $report
PowerShell-аналог (Windows): Сохранить как run.ps1.
# run.ps1
$results = "allure-results"
$report  = "allure-report"

Remove-Item -Recurse -Force $results -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force $report  -ErrorAction SilentlyContinue

pytest --alluredir=$results
allure generate $results -o $report --clean
allure open $report

Пример 7: вложение скриншота при ошибке Selenium

⚠️ Проверить по документации: этот паттерн выходит за рамки лекции — добавлен как практическая рекомендация для UI-тестов с Selenium.
# conftest.py
import allure
import pytest

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    rep = outcome.get_result()
    if rep.failed:
        driver = item.funcargs.get("driver")
        if driver:
            allure.attach(
                driver.get_screenshot_as_png(),
                name="Скриншот при ошибке",
                attachment_type=allure.attachment_type.PNG
            )