В данной статье хочу поделиться содержанием pipeline.jenkinsfile с минимальной необходимостью для организации автоматизированного тестирования. Установку, настройку самого Jenkins мы рассматривать не будем, только pipeline и его содержание для АТ.
Автотесты в нашем случае будут написаны с использованием:
java
maven
junit5
allure
Бонусом расскажу как сделать запуск сборки по действию в GitLab.
Что мы хотим от Jenkins?
Автоматический запуск тестов по расписанию (ночные прогоны регресса)
Хранение самого файла с pipeline в репозитории с исходным кодом автотестов
Использование переменных, параметров и сredentials
Шаги
клонирование репозитория
запуск автотестов с возможностью выбора сьюта
формирование allure-отчета
Нотификация на почту о завершении прогона
pipeline.jenkinsfile
И так, сначала сразу результат, а потом посмотрим на него более детально.
pipeline {
agent {
node {
label 'jenkins-vm.company.ru'
}
}
triggers {
cron('H 7 * * *')
}
parameters{
choice(choices: ['regress', 'smoke'], description: 'Выбор сьюта для запуска', name: 'suiteToRun')
}
environment{
mailRecipients = 'dminakova@cinimex.ru'
TEST_CREDS = credentials('444444444')
}
stages {
stage('clone repo') {
steps{
checkout([$class : 'GitSCM', branches: [[name: '*/master']],
doGenerateSubmoduleConfigurations: false,
extensions : [],
submoduleCfg : [],
userRemoteConfigs : [[credentialsId: '22222222',
url : 'https://git.company.ru/java_example.git']]
])
}
}
stage('run tests') {
steps{
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
withMaven(jdk: 'JDK 8u172', maven: 'Maven 3.6.3') {
sh 'mvn clean install -Dgroups=${suiteToRun}'
}
}
}
}
stage('run allure reports') {
steps {
allure([includeProperties: true,
jdk: '',
properties: [],
reportBuildPolicy: 'ALWAYS',
results: [[path: '**/allure-results']]
])
}
}
}
post {
always{
echo 'Pipeline is complete'
emailext (
subject: "CMXQA.TESTS Отчет прогона тестов [${env.BUILD_NUMBER}] ",
body:"""Подробный allure-отчет: "<a href='${env.BUILD_URL}allure/'>${env.JOB_NAME} [${env.BUILD_NUMBER}]</a>"</p>""",
to: "${env.mailRecipients}"
)
}
}
}
Ссылка на GitHub: https://github.com/DaryaMin/jenkins_pipeline_at/tree/master
Pipeline детально
Рассмотрим pipeline автотестов более детально.
Сборка автотестов чаще всего содержит следующие блоки:
Переменные
Параметры
Credentials
Триггеры сборки
Шаги
клонирование репозитория
запуск автотестов
формирование отчета
Действия после шагов: нотификация
Переменные
Pipeline поддерживает объявление переменных, которые можно в нем дальше использовать.
Для этого используется кодовое слово environment. Переменные перечисляются через запятую по принципу ключ = 'значение'.
В дальнейшем вызываются при помощи ${env.<название переменной>}, например: ${env.mailRecipients}
environment {
allureResults = 'target/allure-results',
allureReportPolicy = 'ALWAYS',
mailRecipients = 'test@cinimex.ru, dminakova@cinimex.ru'
}
к содержанию
Параметры сборки
В Pipeline можно указать некие параметры сборки с возможностью выбора при ручном старте сборки и дефолтным значением для автоматического старта по триггеру. Значения параметров могут использоваться как значения параметров самого приложения, например: url стенда, название сьюта для запуска и т.п.
Кодовое слово - parameters.
Параметры имеют тип, название, значение по умолчанию и описание.
Параметры бывают разных типов:
string
Поддерживает строчный параметр, например:
parameters { string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '') }
text
Может содержать несколько строк, например:parameters { text(name: 'DEPLOY_TEXT', defaultValue: 'One\nTwo\nThree\n', description: '') }
booleanParam
Параметр принимающий значения истина/ложь, например:parameters { booleanParam(name: 'DEBUG_BUILD', defaultValue: true, description: '') }
choice
Параметр, который содержит уже конкретные значения к выбору. Часто используется для выбора сьюта (регресс, смок, дебаг), т.к. названия сьютов редко меняются.parameters { choice(name: 'CHOICES', choices: ['one', 'two', 'three'], description: '') }
password
Параметр предназначенный для пароля.parameters { password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'A secret password')
отдельным типом заслуживающим особенного внимания является gitParameter. Этот тип не входит в базовую версию Jenkins и добавляется плагином Git Parametr.
parameters { gitParameter(name: 'BRANCH', defaultValue: 'develop', description: 'Ветка, из которой деплоимся', branchFilter: 'origin/(.*)', type: 'PT_BRANCH', selectedValue: 'DEFAULT')}
Пример синтаксиса параметров:
pipeline {
agent any
parameters {
string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
text(name: 'BIOGRAPHY', defaultValue: '', description: 'Enter some information about the person')
booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value')
choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something')
password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'Enter a password')
}
stages {
stage('Example') {
steps {
echo "Hello ${params.PERSON}"
echo "Biography: ${params.BIOGRAPHY}"
echo "Toggle: ${params.TOGGLE}"
echo "Choice: ${params.CHOICE}"
echo "Password: ${params.PASSWORD}"
}
}
}
}
к содержанию
Credentials
Обратим внимание, что в выводе в консоль Jenkins нас предупреждает о том, что выводить значение пароля в echo используя Groovy String interpolation не является безопасным.
Как же избежать возможной утечки пароля в данном случае?!
Самый простой вариант - это заменить двойные кавычки на одинарные.
О данной особенности не стоит забывать и при работе с методом credentials().
Результат выполнения
Метод credentials() поддерживает несколько вариантов:
secret text
usernames and passwords
secret files
Jenkins' Snippet Generator для других типов
Создание credential в Jenkins
Перейдем в раздел credentials
Выберем папку
Выбираем домен
Здесь отображаются уже существующие credentials. Их можно отредактировать. Нас интересует команда меню: Add credentials
В нашем случае выберем тип Username and Password. Также существует плагин для Jenkins для интеграции с Vault. Подробней https://plugins.jenkins.io/hashicorp-vault-plugin/
Чек-бокс Treat username as secret не отображает username на списке credentials
ID если не будет заполнено, сформируется автоматически
Для того чтобы credentials можно было использовать, необходимо объявить переменную:
environment {
TEST_CREDS = credentials('4444444444444')
}
здесь TEST_CREDS - это название переменной, а в скобках указан ID credentials
Также стоит отметить, что при обращении по имени переменной данные получаются в формате user:password. При необходимости получения непосредственно user или password к названию переменной добавляется _USR или _PSW соответственно. Объявлять эти переменные дополнительно не требуется.
Обращение к значению переменной осуществляется за счет указания знака$ перед названием переменной.
Пример
Добавим в наш пайплайн credentials и выведем их значение:
pipeline {
agent {
node {
label 'jenkins-vm.company.ru'
}
}
parameters {
string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
text(name: 'BIOGRAPHY', defaultValue: '', description: 'Enter some information about the person')
booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value')
choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something')
password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'Enter a password')
}
environment {
TEST_CREDS = credentials('4444444444')
}
stages {
stage('Example') {
steps {
echo "Hello ${params.PERSON}"
echo "Biography: ${params.BIOGRAPHY}"
echo "Toggle: ${params.TOGGLE}"
echo "Choice: ${params.CHOICE}"
echo 'Password: ${params.PASSWORD}'
echo 'from credentials: $TEST_CREDS'
echo "user from credentials: $TEST_CREDS_USR"
echo "password from credentials: $TEST_CREDS_PSW"
}
}
}
}
Результат выполнения:
В данном случае даже использование двойных кавычек не выводит конкретное значение в консоль. Но все равно интерполяция является небезопасной, о чем нас предупреждает Jenkins. И рекомендуется использовать одинарные кавычки.
к содержанию
Триггеры сборки
Существует несколько вариантов триггеров сборки:
- Удаленный запуск сборки
- Старт сборки после окончания сборки другого проекта
- Запуск периодически
- По изменениям в GitLab
- Триггер из артифактори
- Триггеры GitHub
- Scm изменения
Самый популярный для запуска автотестов - это периодический запуск, например: ежедневно в 7 утра. Чтобы по началу рабочего дня было актуальное состояние прогона тестов.
Пример, ежедневный запуск в рабочие дни в 7.00: H 7 * * 1-5
Синхронизация с GitLab
Полностью строка звучит "Build when a change is pushed to GitLab. GitLab webhook URL: https://company.jenkins.ru/project/TEST/ExampleMVN"
Из названия нам потребуется URL. Именно на этот адрес GitLab будет отправлять события.
По каким же событиям Jenkins стартует сборку?
любой Push в репозиторий
Push в случае удалении ветки
Создание любого Merge Request
Создание Merge Request содержащего новые коммиты
Принятие Merge Request
Закрытие Merge Request
Подтверждение Merge Request
Если Merge Request содержит конкретный коммент
А теперь посмотрим, что нужно сделать со стороны Gitlab.
Нам понадобится URL и Secret Token.
URL - указан в Jenkins когда мы проставляли чек-бокс по триггеру сборки. В нашем случае это https://company.jenkins.ru/project/TEST/ExampleMVN
Secret Token - также берем из Jenkins из настроек триггера сборки по изменениям в Gitlab. Точнее, то значение которое мы укажем в Gitlab должно совпадать с указанным в Jenkins.
Генерация Secret Token
Необходимо перейти в Расширенные настройки триггер:
Нажать Generate
Скопировать сгенерированный код и вставить его в GitLab
После добавления webhook он отобразится внизу страницы
Можно проверить что все настроено корректно, нажав на кнопку Test. В случае если все сделано корректно, то появится уведомление
к содержанию
Этапы/stages
Основное описание того что проиcходит в Pipeline заключается как раз в блоке Stages. Блок Stages должен содержать минимум один этап (stage). Рекомендуется делать этапы дискретными, например: Build, Test, and Deploy.
Каждый этап (stage) содержит:
название, которое отображается в консоле при выполнении. Указывается в круглых скобках в одинарных кавычках
непосредственно шаги выполнения (steps)
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
GIT
Одним из основных этапов сборки что для разработчиков, что для тестирования является интеграция с репозиторием для получения актуального состояния исходного кода. https://www.jenkins.io/doc/pipeline/steps/git/
В данном случае нам помогает плагин Jenkins https://plugins.jenkins.io/git/
Существует два варианта клонирования репозитория:
git step
checkout step
git step является более простым вариантом и соответственно менее функциональным.
checkout step лучше использовать с SCM checkout method.
Рассмотрим простейший вариант git step. Подробнее про SCM checkout method можно прочитать https://plugins.jenkins.io/git/#plugin-content-pipeline-examples
Пример Pipeline, в котором клонируюется репозиторий
pipeline {
agent {
node {
label 'jenkins-vm.company.ru'
}
}
stages {
stage('clone repo') {
steps {
git url: 'https://git.company.ru/java_example.git',
credentialsId: '22222222',
branch: 'master'
}
}
}
}
Пример c checkout:
Stage('clone repo') {
steps{
checkout([$class : 'GitSCM', branches: [[name: '*/master']],
doGenerateSubmoduleConfigurations: false,
extensions : [],
submoduleCfg : [],
userRemoteConfigs : [[credentialsId: '2222222222',
url : 'https://git.company.ru/java_example.git']]]
)
}
}
Запуск тестов
После того как мы клонировали репозиторий с нашими автотестами, необходимо их запустить.
Запускаются они стандартной командой mvn (в данном случае) mvn clean install. Также можно указать версию jdk, maven. Команда запуска полностью дублируется той что мы запускаем у себя локально из консоли. Например: можно добавить запуск конкретного сьюта.
stage('run tests') {
steps{
withMaven(jdk: 'JDK 8u172', maven: 'Maven 3.6.3') {
sh 'mvn clean install'
}
}
}
Запуск конкретного сьюта автотестов
Для этого в тестах у нас имеется аннотация Tag.
Значения SMOKE и REGRESS вынесены в отдельный класс как константы. И уже на их значение настраивается Pipeline
public class SuiteName {
public static final String SMOKE = "smoke";
public static final String REGRESS= "regress";
}
В пайплайн добавим параметр:
parameters{choice(choices: ['regress', 'smoke',],
description: 'Выбор сьюта для запуска',
name: 'suiteToRun')}
Отредактируем команду запуска:
sh 'mvn clean install -Dgroups=${suiteToRun}'
В итоге при запуске сборки запустятся только те автотесты, которые помечены соответствующей аннотацией.
Формирование отчета
После прогона тестов нам бы хотелось получить некий отчет о результатах выполнения автотестов.
Jenkins по умолчанию формирует отчет об упавших тестах, из которого видно:
- Кем был осуществлен запуск
- Сколько по времени выполнялось
- В каком репозитории лежат исходники
- Результаты теста в виде количества упавших тестов и с какой ошибкой, если провалиться в конкретный тест
Данный вариант не особо удобен в использовании и мало информативен для сотрудников, не связанных с написанием автотестов. Более читабельный формат с описанием непосредственно шагов автотеста позволяет сформировать allure report. Jenkins умеет также с ним работать https://www.jenkins.io/doc/pipeline/steps/allure-jenkins-plugin/
Чтобы Allure отчет был информативен нужно уделять внимание исходному коду и аннотациям Allure.
Подробнее про Allure: официальная документация: https://docs.qameta.io/allure/#_junit_5
Для того, чтобы jenkins формировал allure-отчет, необходимо добавить в pipline соответствующий этап:
stage('run allure reports') {
steps {
allure([includeProperties: true,
jdk: '',
properties: [],
reportBuildPolicy: 'ALWAYS',
results: [[path: '**/allure-results']]
])
}
}
В результате у нас появился дополнительный этап и значок allure у сборки
к содержанию
Формирование отчета
После прогона тестов нам бы хотелось получить некий отчет о результатах выполнения автотестов.
Jenkins по умолчанию формирует отчет об упавших тестах, из которого видно:
- Кем был осуществлен запуск
- Сколько по времени выполнялось
- В каком репозитории лежат исходники
- Результаты теста в виде количества упавших тестов и с какой ошибкой, если провалиться в конкретный тест
Данный вариант не особо удобен в использовании и мало информативен для сотрудников, не связанных с написанием автотестов. Более читабельный формат с описанием непосредственно шагов автотеста позволяет сформировать allure report. Jenkins умеет также с ним работать https://www.jenkins.io/doc/pipeline/steps/allure-jenkins-plugin/
Чтобы Allure отчет был информативен нужно уделять внимание исходному коду и аннотациям Allure.
Подробнее про Allure: официальная документация: https://docs.qameta.io/allure/#_junit_5
Для того, чтобы jenkins формировал allure-отчет, необходимо добавить в pipline соответствующий этап:
stage('run allure reports') {
steps {
allure([includeProperties: true,
jdk: '',
properties: [],
reportBuildPolicy: 'ALWAYS',
results: [[path: '**/allure-results']]
])
}
}
В результате у нас появился дополнительный этап и значок allure у сборки, нажав на который мы перейдем непосредственно в allure отчет
к содержанию
Нотификация
Post блок состоит из одного или нескольких шагов выполняющихся либо после конкретного stage либо после всех stage, в зависимости от его расположения в pipeline.
Блок post может содержать следующие условия выполнения:
always
,changed
,fixed
,regression
,aborted
,failure
,success
,unstable
,unsuccessful
,cleanup
.
Подробнее https://www.jenkins.io/doc/book/pipeline/syntax/#post
Мы прогнали автотесты, сформировали отчет, но пока никто не знает об этом.
Наша следующая задача - рассказать об этом всем заинтересованным лицам.
Варианты тут могут быть разные:
нотификация о начале тестирования (не в блоке Post)
нотификация только в случае упавших тестов
нотификация всегда по завершении
прочие варианты, которые можно придумать)
Но все они базируются на процессе нотификации. Рассмотрим вариант нотификации по почте.
Отправка письма на почту
Для отправки письма на почту есть команда emailext(). Подробнее https://plugins.jenkins.io/email-ext/
Добавим в наш pipeline отправку сообщения, указав:
тему сообщения
тело (в теле добавим сразу ссылку на allure отчет)
адресатов
post {
always{
echo 'Pipeline is complete'
emailext (
subject: "CMXQA.TESTS Отчет прогона тестов [${env.BUILD_NUMBER}] ",
body:"""Подробный allure-отчет: "<a href='${env.BUILD_URL}allure/'>${env.JOB_NAME} [${env.BUILD_NUMBER}]</a>"</p>""",
to: "dminakova@cinimex.ru"
)
}
}
Pipeline from SCM
Ну и в завершении, хотелось бы отметить: bestPractice является хранение файла jenkins.pipeline вместе с кодом
В проекте создаем файл в корневом каталоге и называем его piplene.jenkinsfile. И это же название указываем в Script Path.
В самом файле указываем все то что у нас было в jenkins