Часть 2 (вы здесь)
В прошлой части мы написали сборку нашего приложения, но приложение без тестов — плохое приложение. Давайте же их напишем! Но сначала я вам скажу, что закидывать все изменения в main – плохая идея. Обычно есть промежуточная ветка release, а иногда и develop, от которых создают уже ветки для решения конкретных задач: feature/001, feature/002. Давайте же создадим ветку с тестами. Сделать это можно в VS Code. Нажимаем внизу на имя ветки "main", в открывшемся наверху списке выбираем "+ Create new branch…" и вводим ее имя "feature/001_add-tests", а затем жмем Enter.
После этого внизу имя текущей ветки должно измениться с "main" на "feature/001_add-tests". Теперь создадим в корне репозитория папку tests, а в ней файл Сортировка.os следующего содержания:
#Использовать asserts
#Использовать "../src"
Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт
ВсеТесты = Новый Массив;
ВсеТесты.Добавить("Тест_СортировкаМассива5");
// ВсеТесты.Добавить("Тест_СортировкаМассива50");
Возврат ВсеТесты;
КонецФункции
Процедура Тест_СортировкаМассива5() Экспорт
ТестируемыйМассив = ПолучитьМассивДляТеста(5);
РезультатСортировки = Сортировка.Отсортировать(ТестируемыйМассив);
Ожидаем.Что(РезультатСортировки[2]).МеньшеИлиРавно(РезультатСортировки[4]);
КонецПроцедуры
Процедура Тест_СортировкаМассива50() Экспорт
ТестируемыйМассив = ПолучитьМассивДляТеста(50);
РезультатСортировки = Сортировка.Отсортировать(ТестируемыйМассив);
Ожидаем.Что(РезультатСортировки[20]).МеньшеИлиРавно(РезультатСортировки[40]);
КонецПроцедуры
Функция ПолучитьМассивДляТеста(Размер)
Результат = Новый Массив;
ГСЧ = Новый ГенераторСлучайныхЧисел(42);
Пока Результат.Количество() < Размер Цикл
Результат.Добавить(ГСЧ.СлучайноеЧисло(0, 100));
КонецЦикла;
Возврат Результат;
КонецФункции
Да, это то, о чем вы подумали — Unit-тесты для OneScript. Мы добавили два теста, но подключили пока только первый с массивом на 5 элементов. Осталось только подключить его к скрипту, который запускает CI. Для этого изменим текст файла ./ci/scripts/test.os на:
СоздатьКаталог("./report");
ЗапуститьПриложение("1testrunner -runall ./tests xddReportPath ./report", , Истина);
Сообщить("Я протестировался!");
Здесь мы создаем папку для отчета о тестировании, а затем запускаем все тесты в папке ./tests. И чтобы результаты попали в Gitlab, надо изменить Job test в .gitlab-ci.yml на:
test:
stage: test
script: oscript ./ci/scripts/test.os
artifacts:
paths:
- ./report/*.xml
expire_in: 1 week
reports:
junit: ./report/*.xml
Что делает artifacts, мы уже знаем по прошлой статье — сохраняет файлы, соответствующие маске, чтобы потом можно было их использовать. Тут добавлена секция reports – она позволяет сказать gitlab-у, что у нас есть еще и отчеты в формате jUnit и мы хотим увидеть их в Pipeline. Более подробно можно посмотреть в разделе Testing на официальном сайте Gitlab: https://docs.gitlab.com/ee/ci/unit_test_reports.html . Здесь доступны описания форматов файлов с результатами, например, для Code Quality – результатов статического анализа кода, также можно передавать метрики или покрытие кода тестами. Но давайте запустим наш тест. Закоммитим изменения и не забудем сделать push.
После переходим в список Pipelines и видим, что тест запустился. Откроем же сам Pipeline (клик на колонку Pipeline ID), чтобы увидеть результаты:
И видим, что в Pipeline появилась вкладка Tests, где мы можем посмотреть результаты с детализацией по конкретным тестам:
Теперь вернемся в IDE и в файле ./tests/Сортировка.os раскомментируем строку "ВсеТесты.Добавить("Тест_СортировкаМассива50");" Зафиксируем и отправим изменения в удаленный репозиторий. После чего снова откроем список Pipelines в Gitlab. Нам надо подождать завершения тестов:
Но я сразу скажу, что завершения теста можете не ждать: он будет выполняться 1 час, после чего завершится по таймауту. Если тест у вас завершился успешно за пару секунд, вам явно стоит бежать казино и ставить все на зеро. Но откуда у нас этот таймаут? Да еще и Job build не выполнился из-за того, что в тестах были ошибки. Хорошо бы это исправить.
Начнем с таймаута. В правой панели можно увидеть Timeout: 1h (from project) – это стандартный таймаут для любой работы проекта. Указать его можно в настройках. Переходим в Settings → CI/CD → разворачиваем "General pipelines" и прокручиваем до секции Timeout – там указан стандартный 1 час:
Но это таймаут "по умолчанию" и обычно его менять не надо. Нам же надо переопределить его для конкретной работы. Откроем в VS Code .gitlab-ci.yml и допишем в работу test "timeout: 3 hours". Так же здесь добавим поле "allow_failure: true" и тогда при ошибке в Job test пайплайн будет продолжать выполняться, а Job будет отображаться в Pipeline желтым с восклицательным знаком, а не красным с крестом. В результате мы получаем Job вида:
И все же изменим тесты, чтобы сортировать не 50, а 10 значений — есть "небольшое" подозрение, что наш алгоритм сортировки может иметь некоторые недостатки в определенных условиях:
Теперь опять делаем "Stage all changes" → "Commit" → "Push". Переходим в Gitlab и открываем последний Pipeline и Job test в нем, чтобы убедиться, что таймаут теперь 3 часа:
Итак, мы готовы влить эти изменения в основную ветку. Обычно для этого необходимо сделать merge request, ведь ветку main блокируют, чтобы джун случайно не запушил туда нерабочий код. Переходим в "Merge requests" и видим, что Gitlab сам предлагает создать его на последнюю ветку. Жмем "Create merge request":
В открывшемся окне скролим вниз. Там ставим флаг "Squash commits when merge request is accepted", чтобы в основной ветке был только один коммит, а не россыпь промежуточных с неработающим кодом. После нажимаем синюю кнопку "Create merge request". В открывшимся окне нажимаем кнопку "Merge":
Если мы перейдем в список Pipelines после этого, то увидим, что у нас запустилась сборка после мержа:
Обычно нам не нужно, чтобы pipeline запускался на каждый push. Давайте создадим новую ветку, назовем ее "feature/002_rules" и опишем в ней условия запуска. Перед созданием в VS Code новой ветки не забудьте перед этим переключиться на ветку main и получить изменения. Кликните внизу на "feature/001_add-tests" и выбрав в выпавшем списке main:
После этого на вкладке "Source control" делаем Pull. А как создать новую ветку, было описано в начале данной статьи.
Далее создаем новую ветку как было описано в начале статьи. Итак, мы готовы прописать условия запуска для конкретных работ. Если посмотреть в документацию по Gitlab-CI, то можно найти два способа для задания этих условий. Первый опирается на использование секций only и except и его описание начинается с:
Последуем этому совету и посмотрим что же такое rules. В документации данный способ представляет собой массив условий каждому из которых соответствует способ выполнения. Итак в нашем случае предположим, что мы не хотим запускать тесты на ветке main, а в остальных случаях только если хеш коммита содержит d, ведь это счастливое число. Хотя в остальных было бы хорошо иметь возможность запустить его при необходимости вручную. Как это написать? Давайте перепишем работу test следующим образом:
test:
stage: test
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: never
- if: '$CI_COMMIT_SHORT_SHA =~ /[d]/'
when: on_success
- if: '$CI_COMMIT_BRANCH != "main"'
when: manual
script: oscript ./ci/scripts/test.os
timeout: 3 hours
allow_failure: true
artifacts:
paths:
- ./report/*.xml
expire_in: 1 week
reports:
junit: ./report/*.xml
В появившейся секции rules мы описываем напротив if условие, а далее напротив when как должна быть запущена работа. Причем в if мы используем предопределенные переменные CI: CI_COMMIT_BRANCH – ветка на которой был запущен pipeline и CI_COMMIT_SHORT_SHA — первые 8 цифр хеша коммита. Таких переменных много, ознакомится с ними можно в документации Gitlab-CI: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html. Причем доступны они не только в условиях, но и при выполнении сприптов как переменные окружения. В секции when мы можем указать следующие значения:
on_success (default): запускается, если работы предыдущего stage выполнены или для них указано allow_failure: true
manual: для запуска надо будет вручную нажать кнопку run у работы
always: всегда
on_failure: если в предыдущих stage были ошибки
delayed: с указанной задержкой (прописывается в .gitlab-ci.yml)
never: никогда
Давайте же закоммитим эти изменения и отправим в удаленный репозиторий. Чтобы посмотреть как влияет разное значение SHA добавим/удалим пробел в любом файле и отправим коммиты:
Как мы видим, если в хеше коммита нет буквы d для запуска работы надо нажать на кнопку run:
Теперь давайте сделаем merge request и вольем feature ветку в main. Как и было прописано тесты на main при этом не запустились. Но так же мы увидим, что у нас появился pipeline на merge request (как мы его не заметили раньше?!):
Merge request – тоже событие CI. Если мы посмотрим переменные CI, то можно найти CI_PIPELINE_SOURCE – источник события. С помощью этого параметра мы можем определить когда pipeline запущен:
-
push – когда отправляем что-то в удаленный репозиторий в том числе и при завершении merge request
-
api – с использованием API Gitlab (https://docs.gitlab.com/ee/api/pipelines.html)
-
merge_request_event — при создании request и при изменении вливаемой ветки
-
schedule — запущенный по расписанию
Давайте тогда создадим новую ветку (feature/003-mr) и перепишем условия работы на $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_BRANCH == "main" — дадим возможность смотреть результаты тестов тем, кто проводит ревью и после влития в main. Так же добавим условие в build:
build:
stage: build
rules:
- if: $CI_COMMIT_BRANCH == "main"
script: oscript ./ci/scripts/build.os
artifacts:
paths:
- ./possum.exe
expire_in: 1 week
И отправим в удаленный репозиторий. Если перейдем в список pipelines, то мы увидим, что никакой pipeline не сгенерировался — не было ни одной работы, которая подходит под условие. А теперь создадим merge request:
Как мы можем убедиться, теперь ревьювер может видеть результаты теста. Причем если мы отправим еще один коммит по открытому merge request, то pipeline запустится еще раз. Можем убедиться в этом, закоммитив какие-нибудь изменения:
Теперь вмержим merge request и убедимся, что еще раз запустились тесты, а после прошла сборка. В итоге мы теперь можем проверять тесты по каждому merge request – очень удобно.
Конец второй части. Состояние репозитория на момент окончания статьи можно посмотреть по команде:
git clone https://gitlab.com/ffSaschaGff/possum_app.git .
git checkout e408f587ef5db20fa41d8625e500258c20ec4eea