Для выполнения этой стадии необходимо подготовиться.
Устанавливаем sonarqube на сервер. Как вы это сделаете - на ваше усмотрение, я предпочитаю везде по возможности использовать docker, поэтому просто выполняю команду
docker run -d -p 9000:9000 sonarqube
Авторизуемся в админке sonarqube и настраиваем токены, для этого прямо в админке есть подробная инструкция.
Добавьте необходимые строки для работы проекта с sonarqube в файл build.gradle, после чего в gradle у нас появляются новые таски для sonarqube:
После подготовки добавляем новую стадию в наш пайплайн.
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
GIT_DEPTH: "0"
stages:
- feature
- feature-artefact
- feature-clean-stand
- feature-install-stand
- test
feature development:
tags:
- feature
stage: feature
image: gradle:7.5.0-jdk11
script:
- cd rest-service
- ./gradlew assemble
feature docker image:
tags:
- feature
stage: feature-artefact
script:
- cd rest-service
- ./gradlew jibDockerBuild
feature clean stand:
tags:
- feature
stage: feature-clean-stand
allow_failure: true
script:
- cd rest-service/.chart
- helm uninstall docker-compose-feature --namespace=yamangulov-feature
feature install stand:
tags:
- feature
stage: feature-install-stand
script:
- cd rest-service/.chart/
- helm install docker-compose-feature docker-compose --namespace=yamangulov-feature --values=docker-compose/values-yamangulov-feature.yaml
testing:
stage: test
image: gradle:7.5.0-jdk11
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- cd rest-service
- gradle sonarqube -Dsonar.qualitygate.wait=true
rules:
- if: '$CI_BUILD_REF_NAME != "main" && $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event"'
Стадия тестирования разворачивает приложение в docker- контейнере и затем выполняет в gradle таску для проведения unit-тестов (конечно, нужно их написать в приложении) и прочие проверки. После выполнения стадии необходимо зайти в админку и просмотреть результаты проверок (sonarqube очень подробно распишет вам, где что не так), если нужно - доработать код для устранения недостатков и запустить пайплайн снова.
Стадии для smoke и регрессионного тестирования
Сами по себе настройки этих стадий в файле gitlab-ci.yml довольно просты, мы просто поднимаем приложение в контейнере и запускаем нужные таски gradle, которые сами и выполнят нужные тесты:
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
GIT_DEPTH: "0"
stages:
- feature
- feature-artefact
- feature-clean-stand
- feature-install-stand
- test
- smoke
- regress
feature development:
tags:
- feature
stage: feature
image: gradle:7.5.0-jdk11
script:
- cd rest-service
- ./gradlew assemble
feature docker image:
tags:
- feature
stage: feature-artefact
script:
- cd rest-service
- ./gradlew jibDockerBuild
feature clean stand:
tags:
- feature
stage: feature-clean-stand
allow_failure: true
script:
- cd rest-service/.chart
- helm uninstall docker-compose-feature --namespace=yamangulov-feature
feature install stand:
tags:
- feature
stage: feature-install-stand
script:
- cd rest-service/.chart/
- helm install docker-compose-feature docker-compose --namespace=yamangulov-feature --values=docker-compose/values-yamangulov-feature.yaml
testing:
stage: test
image: gradle:7.5.0-jdk11
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- cd rest-service
- gradle sonarqube -Dsonar.qualitygate.wait=true
rules:
- if: '$CI_BUILD_REF_NAME != "main" && $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event"'
smoke testing:
stage: smoke
image: gradle:7.5.0-jdk11
script:
- cd rest-service
- gradle clean integrationTest --tests "*smokeTest*"
rules:
- if: '$CI_BUILD_REF_NAME != "main" && $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event"'
regression testing:
stage: regress
image: gradle:7.5.0-jdk11
script:
- cd rest-service
- gradle clean integrationTest --tests "*regressionTest*"
rules:
- if: '$CI_BUILD_REF_NAME != "main" && $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event"'
Интерес здесь представляют только строки, которые запускают выборочно одну отдельно взятую таску gradle по ее названию и выполняют в ней только один выбранный по шаблону тест.. Сложность этих стадий не в их настройке, а в выполнении самих тестов. И здесь приходят на помощь testcontainers - очень полезная библиотека, которая позволяет поднимать специальные контейнеры для интеграционных тестов. Настройки для подключения библиотеки вы можете увидеть в образце файла build.gradle из предыдущей части этой статьи. Написание самих интеграционных тестов может иногда представлять некоторую сложность и зависит от функционала вашего приложения. Как вариант, приведу в качестве образчика, два класса, описывающие, как это делал я для моего приложения:
PostgresContainerWrapper и TestContainer
В основном можно считать, что для большинства приложений выполненных стадий для пайплайна gitlab ci/cd вполне достаточно, если приложения не предполагают достаточно высокой нагрузки. Если речь идет о высоконагруженном приложении, то без нагрузочных тестов уже не обойтись. Тогда необходимо добавить еще одну стадию для них. Поэтому я планирую через некоторое время дополнить статью четвертой завершающей частью, где и будет описываться мой опыт по выполнению нагрузочного тестирования и включения его в пайплайн.
В заключение приглашаю всех на бесплатный урок от моих коллег из OTUS, где в процессе занятия пройдёмся по метрикам и конечным точкам для работы с приложением. Ощутим мощь актуатора и даже напишем свой "индикатор здоровья". Регистрация на урок доступна по ссылке ниже.