Документация Allure-аннотации в скриптах

Allure-аннотации в скриптах Mockarty

Нагрузочный и функциональный раннеры Mockarty умеют выгружать результаты
в формате Allure 2 — том же, что используют
allure-pytest, allure-java, allure-junit5 и экосистема k6-allure.
Это значит, что бизнес и QA-руководители получают богатый HTML-отчёт со
шагами, вложениями, параметрами и деревом feature → story → epic — прямо
из ваших обычных скриптов.

Это руководство — пользовательский справочник по API аннотаций,
который вы добавляете в скрипты на JavaScript / Go / Python / Java, чтобы
сделать отчёт информативным. Аннотации необязательны — без них вы всё
равно получаете корректный, минимальный отчёт с pass/fail и длительностью.

Когда нужны Allure-аннотации

  • Скрипты гоняются в CI и хочется более богатого артефакта, чем
    JUnit XML.
  • Идёт миграция с k6 + allure-k6 — нужна замена «один-в-один».
  • Стейкхолдеры (PM, QA-лиды) читают HTML-отчёт и хотят навигацию по
    Feature / Story / Owner, а не по сырым URL.
  • К прогонам прикладываются артефакты (скриншоты, payload’ы, заголовки),
    которые неудобно открывать из JSON-файла.

Быстрый пример (JavaScript / k6-совместимый)

В нагрузочном скрипте Mockarty глобал allure инжектится
автоматически. Также его можно импортировать явно из mockarty/allure
(путь k6/allure принимается как алиас) — ради
переносимости скриптов.

import http from 'k6/http';
import { check } from 'k6';

// 'allure' — глобал; либо импорт: import { allure } from 'mockarty/allure'

export const options = { vus: 10, duration: '30s' };

export default function () {
  allure.feature('Checkout');
  allure.story('Apply discount code');
  allure.severity('critical');
  allure.owner('payments-team');
  allure.tag('regression');
  allure.tmsLink('JIRA-1234', 'Apply discount');

  allure.parameter('region', 'eu-west-1');

  allure.step('login', () => {
    const r = http.post('https://api.example.com/login', { user: 'alice' });
    check(r, { 'login 200': (res) => res.status === 200 });
  });

  allure.step('apply discount', () => {
    const r = http.post('https://api.example.com/cart/discount',
                        JSON.stringify({ code: 'SAVE10' }),
                        { headers: { 'Content-Type': 'application/json' }});
    allure.attach('response body', r.body, 'application/json');
    check(r, { 'discount 200': (res) => res.status === 200 });
  });
}

Запуск с Allure-репортером (--allure-results-dir или переменная
окружения ALLURE_RESULTS_DIR):

mockarty-cli perf run script.js --reporter allure --allure-results-dir ./results
allure generate ./results --clean -o ./report

allure generate — это сторонний Allure CLI; Mockarty генерирует
совместимые *-result.json, поэтому любая версия Allure, понимающая
формат 2, отрисует ваш отчёт.

Справочник аннотаций

API повторяет allure-pytest / allure-js. Имена совпадают — документация
этих экосистем тоже подойдёт.

Шаги

allure.step('descriptive name', () => {
  // всё, что внутри — HTTP-вызовы, asserts, etc. — атрибутируется
  // на этот шаг, со start/stop. Шаги вкладываются.
});

Если коллбек бросает — шаг помечается failed; если бросает неожиданный
тип ошибки (опечатка, ReferenceError) — broken. Шаги могут вкладываться
любой глубины; отчёт отрисует дерево.

Вложения (attachments)

allure.attach('payload.json', body, 'application/json');
allure.attach('screenshot.png', pngBytes, 'image/png');
  • Первый аргумент — display-имя в отчёте.
  • Второй — содержимое (string, ArrayBuffer, Uint8Array).
  • Третий — MIME; отчёт по нему выбирает viewer (JSON-prettyprinter,
    image-renderer, plain-text fallback).

Вложения пишутся рядом с JSON-результатом, в JSON хранится только имя
файла. Writer пишет большие вложения потоком; в памяти ограничения нет
сверх того, что разрешает JS-куча, но для многомегабайтных payload’ов
лучше делать сэмплинг.

Иерархические labels

allure.epic('Billing');
allure.feature('Checkout');
allure.story('Apply discount code');
allure.suite('payments-suite');
allure.parentSuite('e2e');
allure.subSuite('eu-region');

Управляют левым деревом навигации в отчёте (Behaviors, Suites).

Метаданные

allure.severity('critical');   // blocker | critical | normal | minor | trivial
allure.owner('payments-team');
allure.tag('regression');
allure.label('framework', 'mockarty');   // свободный k/v

Severity красит строку. Owner — чип. Тэги — фильтрация. Свободный
label(key, value) позволяет проставить что угодно (host, build number,
регион) без плагина.

Title и description

allure.title('Checkout: apply discount code to a 50€ cart');
allure.description('Verifies the SAVE10 promo at the EU pricing tier.');

title — display-имя теста в отчёте. description принимает plain text
или Markdown (HTML-отчёт санитизирует).

Ссылки

allure.link('https://example.com', 'docs', 'link');
allure.issue('JIRA-1234');            // type: issue
allure.tmsLink('TC-456', 'TestRail TC-456');  // type: tms

type маппится на стиль ссылки в Allure (issue / tms / link). Без
name отображается URL.

Параметры

allure.parameter('region', 'eu-west-1');
allure.parameter('vu', '10');

Параметры идут в шапку теста. Allure группирует параметризованные кейсы:
один тест с разными параметрами становится одной строкой с N подзапусками.

Динамические правки

Пространство allure.dynamic.* позволяет задавать значения изнутри
шага (например, после вычисления). Набор методов — тот же:

allure.step('measure', () => {
  const lat = measureLatency();
  allure.dynamic.parameter('latency_ms', String(lat));
  allure.dynamic.severity(lat > 1000 ? 'critical' : 'normal');
});

Схема Allure-результата (wire-формат)

Mockarty пишет <uuid>-result.json в формате Allure 2. Ключевые поля:

Поле Тип Назначение
uuid string Идентичность результата; случайный на запуск.
historyId string Стабильный между запусками; используется для history-sparkline.
testCaseId string Опциональный стабильный id тест-кейса.
fullName string Полное имя (например, script.js#checkout).
name string Display-имя; allure.title() или авто-derived из скрипта.
description string Markdown / plain text.
descriptionHtml string Заранее отрендеренный HTML (Mockarty не заполняет; зарезервировано).
status enum passed / failed / broken / skipped.
statusDetails object { message, trace, known, muted, flaky } для failed/broken.
stage enum Всегда finished в Mockarty (другие — для live-update).
start/stop int64 Unix-millis timestamps.
labels array [{ name, value }, …] — feature/story/epic/severity/owner/tag/…
links array [{ name, url, type }, …]
parameters array [{ name, value }, …]
steps array Дерево шагов; у каждого свой status/start/stop.
attachments array [{ name, source, type }, …] — source — имя файла на диске.

Коллекции (labels, links, parameters, steps, attachments) всегда
сериализуются как [], а не null — ради совместимости с
allure-pytest. Это то, что позволяет интерлевать Go / JS / Python
результаты в одном отчёте.

Канонические имена labels

Отчёт рендерит эти labels с first-class чипами и навигацией; всё
остальное идёт в общую вкладку «labels»:

feature · story · epic · severity · owner · tag ·
suite · parentSuite · subSuite · host · thread ·
framework · language · package · testClass · testMethod ·
AS_ID.

Канонические типы ссылок

issue · tms · link (универсальный).

SDK-примеры

Та же поверхность доступна в SDK на разных языках. Каждый SDK пишет
Allure-JSON локально — RTT к серверу не нужен.

Go

import (
    "context"

    "github.com/mockarty/mockarty-go/allure"
)

func TestCheckout(t *testing.T) {
    ctx, scope := allure.NewScope(context.Background(),
        allure.WithFeature("Checkout"),
        allure.WithStory("Apply discount"),
        allure.WithSeverity(allure.SeverityCritical),
    )
    defer scope.Finish()

    scope.Step("login", func(ctx context.Context) error {
        // … HTTP-вызов …
        return nil
    })

    scope.Step("apply discount", func(ctx context.Context) error {
        body := []byte(`{"code":"SAVE10"}`)
        scope.Attach("payload.json", "application/json", body)
        return nil
    })
}

Python

import allure  # из Mockarty Python SDK

@allure.feature("Checkout")
@allure.story("Apply discount")
@allure.severity("critical")
def test_checkout():
    with allure.step("login"):
        # HTTP-вызов
        pass

    with allure.step("apply discount"):
        body = b'{"code":"SAVE10"}'
        allure.attach(body, name="payload.json", attachment_type=allure.MIME.JSON)

Java

import ru.mockarty.allure.Allure;

@Test
@AllureFeature("Checkout")
@AllureStory("Apply discount")
@AllureSeverity("critical")
void checkout() {
    Allure.step("login", () -> { /* HTTP-вызов */ });
    Allure.step("apply discount", () -> {
        Allure.attach("payload.json", "application/json",
                      "{\"code\":\"SAVE10\"}".getBytes());
    });
}

Имена API в Java- и Python-SDK выровнены с официальными allure-junit5 /
allure-pytest — существующие команды могут копировать привычные паттерны.

Интеграция с CLI

Allure-репортер принимается любой run-командой CLI:

mockarty-cli perf run script.js --reporter allure --allure-results-dir ./results
mockarty-cli test run flow.mockarty.json --reporter cli,allure:./results
mockarty-cli perf run script.js --reporter allure --allure-results-dir ./results

Несколько репортеров одновременно:

mockarty-cli perf run script.js \
  --reporter cli \
  --reporter json:./report.json \
  --reporter junit:./junit.xml \
  --reporter allure --allure-results-dir ./allure-results

После завершения:

allure generate ./allure-results --clean -o ./report
allure open ./report   # открывает локальный браузер

allure — сторонний CLI; ставится со страницы
allurereport.org/docs/install/.
Любая версия, понимающая формат 2 (>= 2.13), отрисует артефакты
Mockarty.

Выборочный прогон (тест-планы)

Тест-план — небольшой JSON-файл со списком тестов, которые нужно
запустить. Именно так работает «перезапустить только упавшие»: вы получаете
план, указываете на него ALLURE_TESTPLAN_PATH — и SDK прогоняет только то,
что в плане перечислено.

Mockarty умеет строить планы сам:

# Только упавшие / сломанные кейсы завершённого запуска
mockarty-cli allure rerun-failed --launch <launch-id> --out ./testplan.json

# Только тесты, затронутые изменением кода
mockarty-cli util ci impact --base origin/main --head HEAD \
  --namespace my-team --allure-out ./testplan.json

Дальше запускайте набор с выставленной переменной:

export ALLURE_TESTPLAN_PATH=$PWD/testplan.json
pytest tests/                 # Python
./gradlew test                # Java (JUnit 5)
go test ./...                 # Go

Файл читают все три SDK — Go, Python и Java. Никаких дополнительных флагов и
настроек: pytest-плагин, JUnit 5-расширение и Go-пакет allure включаются
самим фактом подключения зависимости.

Формат файла

{
  "version": "1.0",
  "tests": [
    {"id": 11111, "selector": "my.company.SimpleTest.simpleTestOne"},
    {"selector": "tests/auth/test_login.py::test_ok"},
    {"id": "CASE-9"}
  ]
}

В каждой записи должен быть id, selector или оба. Тест запускается, если
совпало любое из них:

  • id — Allure-идентификатор теста: @allure.id("777") в Python,
    @AllureId("777") в Java, allure.WithAllureID("777") в Go. Собственные
    привязки Mockarty тоже подходят — @mockarty.testing.test_case("CASE-9") и
    @TestCase("CASE-9"), — поэтому набор на «чистом» Mockarty адресуется по
    идентификатору тест-кейса без Allure-аннотаций.
  • selector — уникальное имя теста. Каждый SDK принимает несколько
    эквивалентных написаний, чтобы совпал план от любого инструмента:
    Язык Какие селекторы принимаются
    Python tests/auth/test_login.py::TestLogin::test_ok, то же без суффикса [param], tests.auth.test_login.TestLogin#test_ok, tests.auth.test_login.TestLogin.test_ok
    Java уникальный id JUnit, com.acme.LoginTest#shouldLogIn, com.acme.LoginTest#shouldLogIn(java.lang.String), com.acme.LoginTest.shouldLogIn, LoginTest#shouldLogIn
    Go TestLogin/happy, example.com/pkg::TestLogin/happy, example.com/pkg.TestLogin/happy, TestLogin#happy

Что происходит в каждой ситуации

Ситуация Что делает SDK
ALLURE_TESTPLAN_PATH не задана Ничего не меняется — идёт обычный полный прогон.
В плане N тестов Запускаются только они, остальные пропускаются.
В плане есть тесты, но ни один не совпал Не выполняется ничего, и это явно помечается — прогон не считается обычным успехом.
План пустой ("tests": []) Не выполняется ничего, прогон считается неуспешным. Пустой план означает «не выбрано ни одного теста», и зелёная галочка тут была бы обманом.
Файла нет, он не читается или это не валидный JSON Прогон останавливается с явной ошибкой. Молчаливого отката к полному прогону не происходит.
MOCKARTY_TESTPLAN_MODE=off План игнорируется, идёт полный прогон — сознательный аварийный выход.

Две строки про отказ — и есть смысл всей механики. Попросить 3 теста и
получить прогон 3000 — или зелёную сборку, в которой не выполнилось ни
одного, — это потраченное время CI и спрятанные падения, поэтому SDK
отказываются и от того, и от другого.

Конкретно:

Язык Пустой план ("tests": []) План ни с чем не совпал Битый или отсутствующий план
Python все тесты снимаются с прогона; pytest завершается кодом 5 («no tests ran») и пишет причину то же — pytest завершается кодом 5 и пишет причину pytest завершается кодом 4 (ошибка использования) ещё до сбора тестов
Go все тесты пропускаются; allure.TestMain возвращает 5 и пишет причину то же — allure.TestMain возвращает 5 тест падает с указанием причины, allure.TestMain возвращает 4
Java discovery останавливается с сообщением, что план ничего не выбрал — сборка падает выполняется ноль тестов, причина печатается; сделать это падением сборки можно через test { failOnNoDiscoveredTests = true } в Gradle или <failIfNoTests>true</failIfNoTests> в Surefire discovery останавливается с ошибкой чтения/разбора — сборка падает

Java — единственное место, где «план ни с чем не совпал» сам по себе не роняет
сборку: JUnit Platform не даёт адаптеру способа завалить прогон после discovery,
поэтому SDK печатает причину и оставляет вердикт переключателю «нет обнаруженных
тестов» в сборочном инструменте. Пустой план роняет сборку — это состояние видно
уже во время фильтрации.

В Go эти коды возвращает allure.TestMain, поэтому подключите его один раз
на пакет:

func TestMain(m *testing.M) { os.Exit(allure.TestMain(m)) }

Без него невыбранные тесты всё равно корректно пропускаются, но go test
напишет привычное ok для прогона, в котором ничего не выполнилось.

Java дополнительно принимает системные свойства вместо переменных окружения —
-Dallure.testplan.path=... и -Dmockarty.testplan.mode=off, — их часто
удобнее задавать из Gradle или Maven. Если заданы оба источника, побеждает
переменная окружения.

Примечания

  • Тест-план — дополнительный фильтр. Он складывается с обычным отбором
    (pytest -k, --tests в Gradle, go test -run): тест запустится, только
    если прошёл оба.
  • Если рядом с плагином Mockarty установлен официальный пакет allure-pytest,
    он читает тот же файл и применяет свой фильтр первым. Он понимает только
    своё написание селектора package.Class#test — поэтому при таком сочетании
    надёжнее планы с id либо с селекторами именно в этой форме.
  • В Go фильтруются тесты, проходящие через allure.T(t, ...). Если набору
    нужен только отбор без отчётности Allure — поставьте первой строкой теста
    allure.SkipIfNotSelected(t).

Загрузка в Mockarty (Окружение, Категории, Исполнитель)

При выгрузке результатов в Mockarty (mockarty-cli allure upload ./allure-results
или mockarty-cli allure watch -- <команда>) обрабатываются и файлы уровня
запуска, которые адаптеры Allure кладут рядом с *-result.json:

Файл Что делает Mockarty
environment.properties / environment.xml Разбирается в панель «Окружение» (ключ/значение) на вкладке Overview отчёта.
categories.json Применяется как классификатор дефектов — упавшие результаты раскладываются по корзинам (например «Product defects» / «Test defects») и показываются на вкладке Категории.
executor.json Показывается карточкой Исполнитель (имя сборки + ссылка) на вкладке Overview.

Эти файлы относятся ко всему запуску (а не к одному тесту), поэтому
выгружаются только при активном launch — сначала создайте его:

eval "$(mockarty-cli allure launch create --launch-name "nightly $(date +%F)")"
mockarty-cli allure upload ./allure-results   # результаты + environment/categories/executor
mockarty-cli allure launch close

launch close завершает launch тем итогом, который Mockarty выводит из
реально пришедших результатов:

Что пришло Записанный статус launch
все результаты прошли (или пропущены) completed
хотя бы один результат упал failed
результатов нет вообще failed — ничего не выгружено, значит launch не зелёный

Если пайплайн действительно прервали — скажите об этом явно:

mockarty-cli allure launch close --abort   # запишет launch как cancelled

Команда печатает записанный вердикт, чтобы в логе CI было видно, чем всё
закончилось:

LAUNCH_CLOSED=8d0c9b2e-... status=completed results=42 failed=0

environment.properties принимает и форму Java .properties
(строки ключ=значение, комментарии #), и форму XML
<environment><parameter> — Mockarty определяет её автоматически.
categories.json использует стандартную схему Allure: массив правил
{name, matchedStatuses, messageRegex, traceRegex}, применяемых к упавшим
результатам по принципу «первое подходящее правило».

Диагностика

Симптом Причина / решение
Отчёт пуст / “No results found” Папка allure-results пустая. Сверьте путь --allure-results-dir с входом allure generate.
Шаги не вложены Забыли передать callback в allure.step — форма без callback’а считается ошибкой.
Attachment отображается как бинарь Не указан или указан неверный MIME. Передавайте application/json, image/png и т.д. явно.
Один и тот же тест появляется дважды Два скрипта эмиттят одинаковый historyId. Либо allure.label('AS_ID', '<unique>'), либо меняйте имя скрипта.
Чипы feature / severity пустые Label задан на шаге, а не на тесте. Вынесите за пределы allure.step(…).
Отчёт рендерится, но labels не появляются в дереве В папке остались старые прогоны. allure generate --clean сбросит их.