Тема
Адаптер JUnit 5
app.doqa:doqa-junit5 отправляет результаты тестов JUnit 5 (Jupiter) в DoQA: автотесты создаются и обновляются сами, результаты приходят с шагами, фикстурами, параметрами, вложениями и ссылками. Адаптер работает в двух режимах — напрямую в Autotest API или файлами (Allure-совместимые артефакты, без сети и без токена в тестовом процессе). Если DoQA недоступен или не настроен, тесты проходят как обычно: ошибка отправки никогда не роняет сборку.
Требования и установка
Нужен JDK 11 или новее (адаптер проверяется на JDK 11, 17 и 21) и JUnit 5 (Jupiter). Добавьте зависимость:
xml
<dependency>
<groupId>app.doqa</groupId>
<artifactId>doqa-junit5</artifactId>
<version>0.1.0</version>
<scope>test</scope>
</dependency>groovy
testImplementation("app.doqa:doqa-junit5:0.1.0")Уже на этом шаге, без какой-либо конфигурации, адаптер пишет результаты в ./results/ — файлы Allure-совместимого формата, которые принимает конвейер загрузки DoQA. Загрузить их в DoQA можно любым способом со страницы Отправка результатов автотестов. Аннотации не обязательны: каждый тест получает стабильный идентификатор автоматически.
Конфигурация
Чтобы отправлять результаты сразу в DoQA, создайте doqa.properties в рабочей директории запуска тестов (для Maven это директория модуля; путь можно переопределить через -Ddoqa.config=… или DOQA_CONFIG) или задайте те же ключи через переменные окружения:
properties
url=https://demo.doqa.app
token=<project-токен>
spaceId=42С этими тремя ключами адаптер переключается в API-режим: сам создаёт прогон и наполняет его — по умолчанию одним батчем в конце, в realtime-режиме — по ходу выполнения. Как создать токен и узнать ID пространства — в разделах «Создание API-токена» и «Как узнать ID пространства».
Источники настроек, по возрастанию приоритета: файл doqa.properties → переменные окружения DOQA_* → JVM-свойства -Ddoqa.*.
Внимание
Токен в конфиге означает, что каждый запуск тестов пишет в DoQA, включая локальные. Обычная схема: локально конфига нет (результаты остаются файлами и никуда не отправляются), а в CI ключи приходят из переменных окружения DOQA_URL / DOQA_TOKEN / DOQA_SPACE_ID.
Все ключи
Ключ (doqa.properties / -Ddoqa.<ключ>) | Переменная окружения | Что это | Дефолт |
|---|---|---|---|
reporting | DOQA_REPORTING | куда слать: api / files / auto / off | auto |
resultsDir | DOQA_RESULTS_DIR | каталог файлового режима | results |
url | DOQA_URL | адрес DoQA | — |
token | DOQA_TOKEN | project-токен | — |
spaceId | DOQA_SPACE_ID | ID пространства | — |
configurationId | DOQA_CONFIGURATION_ID | конфигурация прогона (браузер/ОС и т. п.) | — |
testRunId | DOQA_TEST_RUN_ID | существующий прогон (нужен для режимов 0 и 1) | — |
testRunName | DOQA_TEST_RUN_NAME | имя создаваемого прогона (режим 2) | — |
adapterMode | DOQA_ADAPTER_MODE | режим выбора прогона: 2 — new, 1 — existing, 0 — selective (см. ниже) | 2 |
importRealtime | DOQA_IMPORT_REALTIME | true — стрим результатов по мере прогона (пакет на каждый завершённый класс, вместе с его @AfterAll) | false (батч в конце) |
certValidation | DOQA_CERT_VALIDATION | false — доверять самоподписанным TLS-сертификатам (отключает и проверку hostname) | true |
proxy | DOQA_PROXY | HTTP-прокси, host:port | — |
pipelineId | DOQA_PIPELINE_ID | привязка прогона к CI-пайплайну | авто: CI_PIPELINE_ID / GITHUB_RUN_ID |
branch | DOQA_BRANCH | ветка прогона | авто: CI_COMMIT_REF_NAME / GITHUB_REF_NAME |
batchSize | DOQA_BATCH_SIZE | максимум результатов в одном батч-запросе | 100 |
requestTimeoutMs | DOQA_REQUEST_TIMEOUT_MS | таймаут HTTP-запроса, мс | 30000 |
retries | DOQA_RETRIES | попыток на запрос (повторяются только безопасные запросы) | 3 |
retryBackoffMs | DOQA_RETRY_BACKOFF_MS | базовая пауза между попытками (растёт экспоненциально), мс | 500 |
maxTraceLength | DOQA_MAX_TRACE_LENGTH | лимит длины стек-трейса в результате, символов | 100000 |
maxMessageLength | DOQA_MAX_MESSAGE_LENGTH | лимит длины сообщений, символов | 10000 |
maxParameterLength | DOQA_MAX_PARAMETER_LENGTH | лимит длины значений параметров, символов | 2000 |
pipelineId и branch подхватываются автоматически из стандартных переменных GitLab и GitHub Actions — прогон в DoQA привязывается к пайплайну и ветке без настройки.
Куда уходят результаты: reporting
| Значение | Что происходит |
|---|---|
auto (дефолт) | есть url+token+spaceId — API; нет — файлы |
api | только API (без конфига — предупреждение в лог) |
files | только файлы Allure-совместимого формата в resultsDir |
off | адаптер выключен полностью |
Файловый режим — путь CI-артефактов: тестовому процессу не нужны ни сеть до DoQA, ни секреты. Файлы забирает следующий шаг пайплайна и загружает их в DoQA (см. пример ниже).
Режимы работы с прогоном: adapterMode
2/ new (дефолт) — адаптер сам создаёт прогон и отправляет всё в него. Вариант для CI «просто прогони всё».1/ existing — всё отправляется в существующий прогонtestRunId(его создали заранее — в интерфейсе или через API). ЕслиtestRunIdзадан, аadapterModeне задан явно, адаптер сам работает в этом режиме: указанный прогон никогда не игнорируется молча.0/ selective — селективный прогон: адаптер спрашивает у DoQA, какие автотесты числятся в прогонеtestRunId, и физически исполняет только их — остальные тесты исключаются ещё на этапе discovery. Этим режимом DoQA перезапускает выбранные тесты при запуске из интерфейса.
При запуске пайплайна из DoQA переменные DOQA_TEST_RUN_ID и DOQA_ADAPTER_MODE передаются пайплайну автоматически — адаптер читает их как обычные настройки, отдельная конфигурация не нужна.
Порядок прохождения по плану DoQA
Селективный список приходит от DoQA упорядоченным — в порядке набора запуска. Адаптер умеет исполнять тесты в этом порядке, но включить план-ориентированные orderer'ы можете только вы — JUnit Jupiter не позволяет адаптеру навязать их программно:
properties
# src/test/resources/junit-platform.properties
junit.jupiter.testclass.order.default=app.doqa.junit5.DoqaPlanClassOrderer
junit.jupiter.testmethod.order.default=app.doqa.junit5.DoqaPlanMethodOrdererМетоды внутри класса идут по позиции их внешнего ID в плане; классы — по минимальной позиции их методов (Jupiter исполняет тесты класс-блоками, межклассовое чередование методов невозможно). Тесты вне плана стабильно уходят в хвост; порядок никогда не меняет состав прогона. Без этих properties, без DoQA-сеанса или без плана orderer'ы — строгий no-op.
Ограничения: только Jupiter-engine; порядок гарантируется только при последовательном исполнении (junit.jupiter.execution.parallel.enabled=false — дефолт).
Разметка тестов
Вся разметка опциональна:
java
@DoqaLabels({"regression"}) // класс-уровень: наследуется всеми тестами
class LoginTests {
@Test
@DoqaId("LOGIN-1") // стабильный внешний ID автотеста (рекомендуется)
@DoqaTitle("Успешный вход")
@DoqaDescription("Проверяет happy-path входа по паролю")
@DoqaDisplayName("Вход по паролю") // имя автотеста (иначе — display name JUnit)
@DoqaLabels({"smoke"}) // объединится с класс-уровнем
@DoqaTags({"ui"})
@DoqaLinks({@DoqaLink(url = "https://tracker/BUG-77", type = "defect", title = "флак на CI")})
@DoqaCaseIds({1041}) // привязка к тест-кейсам DoQA (можно несколько)
void loginHappyPath() { /* … */ }
}Ещё две аннотации управляют местом теста в дереве каталога: @DoqaNamespace (по умолчанию — пакет) и @DoqaClassName (по умолчанию — простое имя класса). Обе работают и на уровне класса. Аннотации живут в пакете app.doqa.annotations, рантайм-фасад — app.doqa.Doqa: они общие для всех JVM-адаптеров DoQA, смена фреймворка не потребует править импорты.
Если @DoqaId нет, идентификатор ищется в таком порядке: [DOQA-123] или @DOQA:123 в display name → совместимый ID из Allure-разметки (Переезд с Allure) → детерминированный хэш сигнатуры метода. История автотеста стабильна в любом случае; явный ID делает её устойчивой ещё и к переименованиям. Что происходит с историей и привязками на стороне DoQA — на странице Автотесты.
Параметризованные тесты
Аргументы каждой инвокации уходят как именованные параметры результата:
java
@ParameterizedTest
@ValueSource(strings = {"chrome", "firefox"})
void worksIn(String browser) { /* … */ } // parameters: [{name: "browser", value: "chrome"}]Чтобы получить отдельный автотест на каждую инвокацию, используйте плейсхолдер {имяАргумента} в любой аннотации:
java
@ParameterizedTest
@ValueSource(strings = {"chrome", "firefox"})
@DoqaId("LOGIN-IN-{browser}") // → LOGIN-IN-chrome, LOGIN-IN-firefox
@DoqaTitle("Вход в {browser}")
void loginIn(String browser) { /* … */ }Аргументы инвокаций захватывает расширение DoqaExtension — включите автодетект расширений (см. Фикстуры и шаги), иначе плейсхолдер останется нераскрытым. А чтобы имена аргументов были настоящими (browser, а не arg0), включите флаг компилятора -parameters.
Рантайм-API из тела теста
java
import app.doqa.client.LinkType;
Doqa.step("открыть страницу", () -> page.open()); // шаг (вложенные — просто вкладывайте)
int sum = Doqa.step("посчитать", () -> a + b); // шаг со значением
Doqa.step("чекпоинт пройден"); // мгновенный passed-шаг без тела
Doqa.step("создать заказ", "POST /orders", () -> order()); // шаг с описанием
Doqa.addParameter("env", "staging");
Doqa.addAttachments("target/screenshot.png"); // файл к тесту или открытому шагу
Doqa.addAttachment("response.json", bytes, "application/json"); // вложение из памяти
Doqa.addAttachment("app.log", logText); // текстовое вложение (text/plain)
Doqa.addLink("https://jira/TASK-5", LinkType.REQUIREMENT);
Doqa.addLink(url, type, title, description); // расширенная форма; есть и addLinks(Link...)
Doqa.addMessage("покупатель создан через фабрику");
Doqa.addCaseIds(1042); // привязка к тест-кейсу из кода
Doqa.addExternalId("CART-DYN-1"); // стабильный ID динамического (@TestFactory) теста
Doqa.addTitle("…"); Doqa.addDescription("…"); Doqa.addDisplayName("…");
Doqa.addLabels("…"); Doqa.addTags("…");
Doqa.addLabel(Labels.SEVERITY, "critical"); // key:value-метки (severity/owner/epic/…)Все вызовы безопасны: вне активного теста или при reporting=off они просто ничего не делают. Нативные JUnit @Tag автоматически попадают в теги автотеста — двойная разметка не нужна.
Шаги и вложения из потоков, которые тест порождает сам (async-код, свои executor'ы), нужно явно перенести в контекст теста:
java
Doqa.Context ctx = Doqa.captureContext();
executor.submit(() -> Doqa.runWith(ctx, () -> Doqa.step("проверка из воркера")));Фикстуры и шаги @Step
Адаптер работает и без этого, но с двумя настройками отчёт полный.
Фикстуры и параметры инвокаций: автодетект расширений
properties
# src/test/resources/junit-platform.properties
junit.jupiter.extensions.autodetection.enabled=true(или точечно @ExtendWith(DoqaExtension.class) на классе). Это даёт:
@BeforeEach/@AfterEach— блоки setup/teardown в каждом результате;@BeforeAll/@AfterAll— класс-фикстуры у всех тестов класса;- шаги и вложения внутри класс-фикстур попадают в узел фикстуры;
- именованные параметры и плейсхолдеры
{param}.
Декларативные шаги @Step: AspectJ-агент
java
@Step("авторизоваться под {user}") // {param}-плейсхолдеры из аргументов метода; без текста — имя метода
void authorize(String user) { /* … */ }Для @Step подключите AspectJ-агент к тестовой JVM (адаптер не приносит его транзитивно):
xml
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.24</version>
<scope>test</scope>
</dependency>
<!-- maven-dependency-plugin пишет путь к jar-файлу в property ${org.aspectj:aspectjweaver:jar} -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions><execution><phase>initialize</phase><goals><goal>properties</goal></goals></execution></executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-javaagent:${org.aspectj:aspectjweaver:jar} --add-opens java.base/java.lang=ALL-UNNAMED</argLine>
</configuration>
</plugin>groovy
configurations { doqaAgent }
dependencies { doqaAgent "org.aspectj:aspectjweaver:1.9.24" }
test {
jvmArgs "-javaagent:${configurations.doqaAgent.singleFile}",
"--add-opens", "java.base/java.lang=ALL-UNNAMED"
systemProperty "junit.jupiter.extensions.autodetection.enabled", "true"
}Не хотите агент — используйте явный Doqa.step("…", () -> …), он работает всегда. Версия weaver'а определяет максимальную поддерживаемую версию байткода: для новых JDK берите актуальный aspectjweaver (1.9.24 покрывает JDK до 24 включительно).
Совместимость с Allure
Проект, размеченный Allure-аннотациями, адаптер подхватывает без правок кода. Что именно поддерживается, чем становится идентификатор и как постепенно переехать на разметку @Doqa* — Переезд с Allure.
Маппинг исходов
| Что случилось | Исход в DoQA |
|---|---|
| Тест прошёл | passed |
Упала проверка (AssertionError, AssertJ, opentest4j) | failed |
| Любое другое исключение (инфраструктура, NPE, таймаут) | broken |
@Disabled / assumption | skipped |
Различие failed и broken важно: кластеризация ошибок и аналитика нестабильных (flaky) обрабатывают их по-разному.
Запуск в CI
yaml
# .gitlab-ci.yml — DOQA_URL, DOQA_TOKEN, DOQA_SPACE_ID заданы в переменных CI/CD проекта
test:
image: maven:3.9-eclipse-temurin-17
script:
- mvn -B testyaml
# .gitlab-ci.yml — тестовой джобе не нужны ни сеть до DoQA, ни секреты
test:
image: maven:3.9-eclipse-temurin-17
script:
- mvn -B test # адаптер пишет results/
artifacts:
paths:
- results/В API-варианте адаптер сам создаёт прогон и привязывает его к пайплайну и ветке (переменные CI подхватываются автоматически). В файловом варианте каталог results/ загружает в DoQA следующий шаг пайплайна — способы описаны на странице Отправка результатов автотестов. Для GitLab DoQA умеет сама завести DOQA_URL/DOQA_TOKEN/DOQA_SPACE_ID в переменных проекта — см. Подключение CI-систем.
Как результаты доставляются
По умолчанию адаптер копит результаты и в конце прогона отправляет их чанками по batchSize; сбой одного чанка теряет только его. При importRealtime=true каждый завершённый тестовый класс уходит сразу — вместе со своим @AfterAll, поэтому teardown не теряется. Повторяются только безопасные запросы. Ошибка отправки никогда не роняет сборку — адаптер пишет WARNING в лог и продолжает.
Траблшутинг
Частые симптомы и лечение
| Симптом | Причина и лечение |
|---|---|
| Результатов нигде нет | reporting=api без url/token/spaceId — смотрите WARNING в логе; либо reporting=off |
Результаты в results/, а ждали в DoQA | это auto без API-конфига — задайте url/token/spaceId |
@Step-шаги не появляются | не подключён -javaagent:aspectjweaver (см. выше) |
NoSuchMethodError: DoqaStepAspect.aspectOf() | вы сузили вивинг своим aop.xml и исключили аспект — верните <include within="app.doqa.aspects.DoqaStepAspect"/> |
| На JDK 16+ падает вивер / нет шагов | добавьте --add-opens java.base/java.lang=ALL-UNNAMED к argLine |
Параметры называются arg0, arg1 | включите -parameters у компилятора |
| Нет setup/teardown блоков | не включён автодетект расширений (см. «Фикстуры и шаги @Step») |
| Самоподписанный сертификат | certValidation=false — только для тестовых стендов |
| Локальные прогоны спамят прогонами в DoQA | уберите токен из локального конфига или поставьте локально reporting=files |
Смотрите также
- Адаптеры для тестовых фреймворков — когда адаптер, а когда отчёты
- Автоматика запусков — расписания, авто-ретрай упавших, критерии приёмки прогона
- Первый запуск автотестов — сценарий первой интеграции с DoQA