Всем привет! Меня зовут Павел, я технический лидер тестирования в Альфа-Банке в направлении мобильной разработки.
Через плагин withAllureUpload для Jenkins нельзя из нескольких джоб залить отчёт автотестов в один запуск TestOps. Готовых решений в интернете не нашёл, и даже поддержка TestOps не смогла нормально подсказать, как из нескольких Jenkins Jobs отправлять результат в один запуск TestOps. Методом проб и ошибок это сделать удалось — в статье расписал решения, как это работает через терминальную программу.
Относительно недавно, в начале года Банк переехал на новую ТМС Allure Testops. Автотесты (сейчас их >200) запускаются через CI Jenkins, и запускаются параллельно на нескольких девайсах одновременно.
Во время пилотного запуска мы столкнулись с рядом неудобств при запуске всех автотестов в одной джобе, не считая того, что разбирать логи в таких джобах очень сложно. Поэтому начали искать решение, как разбить запуск автотестов на несколько джоб, но чтобы результаты из этих джоб пушились в один агрегированный запуск в TestOps.
Изначально решили скопировать все результаты нисходящих джоб в восходящую и уже из одной Jenkins джобы отправить результаты в один запуск TestOps. Но столкнулись с такой проблемой, что дефолтный плагин TestOps withAllureUpload так не работает. Вместо этого мы получали пустой запуск без результатов.
Далее попробовали передавать параметр ALLURE_JOB_RUN_ID. (этот параметр передаётся от TestOps в Jenkins джобу при создании джобы из TestOps) из восходящей джобы в нисходящую, а уже из нисходящих джоб отправлять через плагин withAllureUpload. Но нисходящая джоба даже не запускалась, а падала с ошибкой. Потому что плагин создаёт новый объект, а если объект уже есть, то плагин падает с ошибкой.

После поисков везде, где только возможно, и множества попыток, решение было найдено в документации самого TestOps. Делается это через allurectl — приложение командной строки. Но предыдущие неудачные попытки также принесли свои плоды. Смогли проработать два решения отправки результатов автотестов в один агрегированный запуск TestOps из разных Jenkins джоб, которые описал ниже.
Способ №1. Отправка результатов из нисходящих Jenkins джоб в один агрегированный запуск TestOps
Этот способ подходит, когда вы создаёте запуск из TestOps и в восходящей Jenkins джобе есть параметр ALLURE_JOB_RUN_ID., который передаётся нисходящим джобам. Таким образом, каждая нисходящая джоба может передавать свои результаты автотестов сразу после своего завершения. Однако при таком флоу в нисходящих Jenkins джобах не будет генерироваться ссылка на запуск TestOps, как показано на скринах ниже.


Настройки восходящей джобы
В pipeline восходящей джобы нужно при запуске нисходящей джобе передать параметр ALLURE_JOB_RUN_ID..
stage('Android Tests SmzUserGoToSmzScreenFromMainPageTest') { steps { script { smzBuild1 = build job: params.DOWNSTREAM_JOB, parameters: [ string(name: 'branchName', value: 'master'), string(name: 'reportPortalTags', value: 'scheduledRegressLaunch'), string(name: 'platform', value: 'ANDROID'), string(name: 'selenoidDeviceName', value: 'Samsung Galaxy A55 5G,Xiaomi Redmi Note 13 Pro,Google Pixel 4a,Samsung Galaxy S21+ 5G'), string(name: 'selenoidEmulatorVersion', value: '13.0'), string(name: 'isRetryEnable', value: 'false'), string(name: 'useLatestRCBuild', value: 'true'), string(name: 'rpLaunchName', value: 'AM.Smz_entry_point_cycle'), string(name: 'runTest', value: 'SmzUserGoToSmzScreenFromMainPageTest'), string(name: 'runInSelenoid', value: 'true'), string(name: 'ALLURE_JOB_RUN_ID', value: "${params.ALLURE_JOB_RUN_ID}") //Передаём., как параметр ALLURE_JOB_RUN_ID ], propagate: false echo "SMZ build number - ${smzBuild1.number}" // Исправлена переменная } } }
Настройки нисходящей джобы
Состоит из двух шагов:
№1. Сначала скачиваем приложение командной строки allurectl.
stage('Install allurectl') { steps { script { sh ''' if ! command -v allurectl &> /dev/null; then echo "Installing allurectl..." curl -sL https://github.com/allure-framework/allurectl/releases/latest/download/allurectl_linux_amd64 -o allurectl chmod +x allurectl ./allurectl --version else echo "allurectl already installed" fi ''' } } }
№2. Затем настраиваем отправку результатов автотестов в агрегированный запуск TestOps.
stage('Running tests') { steps { script { // Дефолтное значение для запуска всех тестов в проекте def testToRun = "ru.testing.*" println("Chosen test parameter as ${params.runTest}") if ("${params.runTest}" != "") { // Для запуска конкретного теста testToRun = "${params.runTest}" } else if ("${params.runCycle}" == FULL_REGRESS) { // Для запуска всех регрессных тестов testToRun = "ru.testing.regress.*" gradleArgs.remove("-Dorg.gradle.project.tags=FULL_REGRESS") } withCredentials([string(credentialsId: 'testops-token', variable: 'ALLURE_TOKEN')]) { sh """ # Запуск тестов с мониторингом через allurectl ./allurectl watch \ --endpoint https://testops \ --project-id 4 \ --launch-name "AM.Regress-#${env.BUILD_NUMBER}" \ --launch-tags "Jenkins_Android,regression" \ --results build/allure-results -- ./gradlew --info clean test ${gradleArgs.join(' ')} --tests "${testToRun}" """ } } } post { always { allure includeProperties: false, jdk: '', results: [[path: 'build/allure-results']] } } }
Так как мы передали параметр ALLURE_JOB_RUN_ID. нисходящей джобе, allurectl не будет создавать новый объект, а запушит отчёты автотестов в уже существующий запуск TestOps.
Полезная ссылка: официальная документация TestOps.
Способ №2. Отправка результатов из восходящей Jenkins джобы в один агрегированный запуск TestOps
Этот способ подходит, когда вы запускаете джобу из Jenkins, и вам нужно отправить результаты автотестов из нескольких нисходящих джоб. В этом случае у восходящей джобы нет параметра ALLURE_JOB_RUN_ID. и передавать нисходящим джобам нечего.
Поэтому флоу для такой передачи будет несколько иным. Также для этого способа нужен установленный в Jenkins плагин copyArtifacts, чтобы можно было копировать результаты нисходящих джоб в восходящую.
Настройка восходящей джобы
В восходящей джобе, как и в прошлом решении, не будет сгенерирована ссылка на запуск TestOps.
№1. Добавляем в pipeline запуск нисходящих джоб.
stage("Android Tests ${cycle}") { downstreamJob = build job: params.DOWNSTREAM_JOB, parameters: [ string(name: 'branchName', value: 'master'), string(name: 'platform', value: 'ANDROID'), string(name: 'selenoidDeviceName', value: selenoidDeviceNameList), string(name: 'selenoidEmulatorVersion', value: '13.0'), booleanParam(name: 'isRetryEnable', value: 'false'), string(name: 'useLatestRCBuild', value: 'true'), string(name: 'rpLaunchName', value: cycle), string(name: 'runCycle', value: cycle), string(name: 'runTest', value: ''), string(name: 'runInSelenoid', value: 'true') ], propagate: false // Проверка на null перед использованием if (downstreamJob?.number) { echo "Build number for ${cycle}: ${downstreamJob.number}" } else { echo "Build for ${cycle} failed to start" } }
№2. Вторым пунктом добавляем шаг сбора логов из нисходящих джоб.
stage("Copy Results From ${cycle}") { script { if (downstreamJob?.number) { copyArtifacts( projectName: params.DOWNSTREAM_JOB, selector: specific("${downstreamJob.number}"), filter: 'build/allure-results/**/*', target: ".", flatten: false ) sh "ls -la build/allure-results/" } else { echo "No artifacts to copy for ${cycle}" } } }
№3. Устанавливаем приложение командной строки allurectl.
stages { stage('Install allurectl') { steps { script { sh """ if ! command -v ./allurectl &> /dev/null; then echo "Installing allurectl..." curl -sL https://github.com/allure-framework/allurectl/releases/latest/download/allurectl_linux_amd64 -o allurectl chmod +x allurectl ./allurectl --version else echo "allurectl already installed" fi """ } } }
Этот шаг может быть любым по счету, главное, чтобы он выполнялся до отправки результатов в TestOps. Здесь уже кому как больше нравится.
№4. И, наконец, отправляем результаты в TestOps.
stage("Push report ${cycle}") { script { if (downstreamJob?.number) { withCredentials([string(credentialsId: 'testops-token', variable: 'ALLURE_TOKEN')]) { def uploadOutput = sh( script: """ ./allurectl upload \ --endpoint https://testops \ --project-id 4 \ --launch-name "AM ANDROID Regress - #${env.BUILD_NUMBER}" \ --launch-tags "Android,AM,regress" \ "build/allure-results" """, returnStdout: true ).trim() if (uploadOutput =~ /Report link: /) { reportUrl = (uploadOutput =~ /Report link: (https?:\/\/\S+)/)[0][1] } currentBuild.description = "Ссылка на запуск Allure TestOps - ${reportUrl}" } sh "rm -f build/allure-results/*" //очищаем директорию с результатами } } }
[!NOTE]
Обратите внимание, что при отправке отчетов без запуска автотестов нужно использовать командуupload, а неwatch, как в способе №1
[ВАЖНО!]
После отправки результатов в TestOps обязательно очищайте директориюbuild/allure‑results, иначе в TestOps будут дублироваться результаты автотестов!
Настройка нисходящей джобы
В нисходящей джобе нужно добавить в pipeline только архивирование сырых результатов allure после автотестов.
post { always { // Архивируем сырые результаты для копирования archiveArtifacts( artifacts: 'build/allure-results/**/*', fingerprint: true, allowEmptyArchive: true ) allure includeProperties: false, jdk: '', results: [[path: 'build/allure-results']] } }
Также у нисходящих джоб будет работать плагин withAllureUpload, и результаты каждой нисходящей джобы также можно отправлять в TestOps отдельным запуском! Здесь уже как вам нужно и/или как нравится.
Полезные ссылки:
Подведение итогов
Есть как минимум два способа отправить результаты автотестов из нескольких Jenkins джоб в один агрегированный запуск TestOps. Надеюсь статья будет полезной. Спасибо за внимание и до новых встреч!!!
ссылка на оригинал статьи https://habr.com/ru/articles/913716/
Добавить комментарий