Приключение на 20 минут. Часть 1: автоматизируем запуск проектов в SoapUI
Всем привет, меня зовут Вероника Дюкарева. Я работаю старшим инженером‑программистом в компании Bercut.
Моя команда разрабатывает и поддерживает системы, использующие протокол SOAP. Каждый релиз включает в себя разработку, unit‑тестирование (делает разработчик), функциональное тестирование (выполняет тестировщик). По результатам последнего создаются автотесты. Про их структуру, а также наш опыт работы с TestIT можно посмотреть в записи митапа.
Статья разделена на две части: в этой мы рассмотрим построение инфраструктуры для запуска SoapUI‑проектов, а в следующей разберемся с их внутренней структурой.
Информация будет полезна всем, кто работает с SoapUI.
Все написанное ниже было испытанно на SoapUI версии 5.7.2.
Примеры кода будут приводиться на Python 3.10 и с библиотекой lxml. Она позволяет выбрать разные подходы к обработке XML; мне наиболее удобным показался objectify.
Предыстория
Когда я пришла в нашу команду, первоочередной задачей была автоматизация регресса. В какой‑то момент техдолг на разработку автотестов закончился, мы научили статистику считаться без нашего участия, а стенды стали обновляться по нажатию кнопки. Тогда пришла пора юнит‑тестов (далее UT, unit‑тесты).
В поддерживаемых нами системах у каждого сервиса могло быть несколько API‑методов. Изначально было удобно создавать один SoapUI‑проект для одного сервиса, но постепенно мы пришли к формату один проект — один метод. У каждого разработчика был свой взгляд на то, как должны выглядеть тесты. Запуск уже существующих также оставался на его совести. Со временем образовались новые, «современные» UT и старые проекты. Особенно чувствительны были доработки в тех из них, которые не изменялись несколько лет: возникали конфликты при обновлении интерфейсов, структуры и переменные не соответствовали новым требованиям. В общем, типичные проблемы легаси. Моей отдельной болью был дебаг и определение места с ошибкой: это баг в коде или я тесты неправильно написала?, а может mock не подхватил изменения? или где‑то не поменяла endpoint?, а может это стенд косячит?
Так появилась задача на автозапуск прогона SoapUI‑проектов, а заодно и их рефакторинга.
Немного про SoapUI
SoapUI инструмент довольно неоднозначный. С одной стороны, он обладает широким набором возможностей для тестирования: поддержка SOAP‑ и HTTP‑протоколов, простое создание mock‑сервисов, настройка маршрутизации ответов, использование скриптов на Groovy, подстановка переменных в запросы и ответы. С другой стороны, в нем достаточно багов, требующих костылей.
Проект в SoapUI представляет собой XML‑файл, в котором хранятся данные об интерфейсах, тестах, настройках и других параметрах. Такой формат позволяет применять к проектам привычные инструменты для работы с XML. Тем не менее, при использовании системы контроля версии, такой как Git, могут возникать трудные для разрешения конфликты, особенно когда несколько разработчиков одновременно работают над проектом.
SoapUI — это ПО с открытым исходным кодом от SmartBear Software под лицензией EUPL v1.1, то есть его можно свободно распространять, модифицировать и использовать. У него есть платная версия, которая называется ReadyAPI. Код, текст лицензии и прочую информацию можно найти в репозитории на GitHub.
Открытый код SoapUI позволяет расширять инструмент под свои задачи. Там можно подсмотреть возможности, о которых не сказано в документации. Например, можно узнать о ключах для запуска или о том, к каким объектам вы можете получить доступ из Groovy‑скриптов.
Если «родной» документации не хватает, то можно воспользоваться материалами его платного брата ReadyAPI: большинство функций есть в каждом из приложений.
Другой способ — генерация с нуля с помощью mvn javadoc: javadoc
О папках и логировании
Во время работы с SoapUI естественным образом возникают различные ошибки. Для исправления одних достаточно устранить опечатки пользователей, другие же можно решить с помощью методов «перезапустить SoapUI — перегрузить проект» или «сохранить проект — запустить тест». Если эти способы не работают, то следует изучить логи (к слову, не самые понятные и полные). Зачастую ошибки не выводятся в графическом интерфейсе, а сохраняются в файлы. Проект может внезапно перестать открываться из‑за некорректных исходников, в который утилиты для автоматизации внесли правки.
В качестве примера возьмем сущность mockResponse. Для параметра WS‑A предлагается выбор из двух предложенных в GUI значений: 200 508 и 200 408. Пользователь может легко обойти это ограничение и вписать свою версию прямо в XML‑файл. SoapUI позволит его открыть, однако, этот mockResponse больше не будет доступен к редактированию. Узнать о проблеме можно будет только в лог‑файлах.
Чаще всего актуальные логи хранятся в домашней директории пользователя в скрытой папке.soapui. В этой же директории находится файл soapui‑settings.xml, содержащий системные настройки. В графическом интерфейсе они расположены во вкладке Preferences. Также можно проверить папку с файлами приложения (C:\Program Files\SmartBear\SoapUI-5.7.0 или в похожей директории), где расположен README с ссылками на сайт с официальной документацией, информация о лицензии, деинсталлятор и так далее.
Наиболее интересной будет папка bin. В ней хранятся файлы с логами soapui‑errors.log и soapui.log, а если вы работаете с Groovy, то и global‑groovy.log. Этот список не исчерпывающий: некоторые объекты SoapUI могут генерировать свои файлы. Уровень логирования можно настроить в soapui‑log4j.xml.
Запуск тестов
Обычно наш разработчик включал mockService через GUI, а затем запускал нужную ему сущность: testCase, testStep, testSuite или весь проект. Задача автозапуска unit‑тестов подразумевала как сохранение возможности работать с проектом через графический интерфейс, так и без него.
Для запуска SoapUI из консоли используют скрипты из папки bin:
testrunner
toolrunner
loadtestrunner
mockservicerunner
securitytestrunner
Каждый из них содержит запуск соответствующего Java‑приложения, например:
"%JAVA%" %JAVA_OPTS% com.eviware.soapui.tools.SoapUIMockServiceRunner %*
Там же можно настроить опции для запуска JVM. Один из самых простых случаев, когда это может понадобиться — некорректное отображение русского языка.
По умолчанию используется такой набор параметров:
set JAVA_OPTS=-Xms128m -Xmx1024m -Dsoapui.properties=soapui.properties "-Dsoapui.home=%SOAPUI_HOME%\"
Для автоматизации прогона unit‑тестов подойдут testrunner и mockservicerunner. В рамках моей задачи хотелось управлять тестами через одну точку входа, то есть запускать один скрипт, а не несколько. Я могла сделать рефакторинг и доработать тесты, поэтому остановилась на использовании только testrunner.
Ключи для запуска скриптов следует искать в документации для ReadyAPI или в исходном коде. В <ваша_папка_с_кодом_name>/soapui/src/main/java/com/eviware/soapui/tools находятся классы *Runner.java, в которых, в свою очередь, расположены методы initCommandLineOptions с инициализацией настроек из командной строки.
Пример аргументов командной строки для скрипта toolrunner
Docker
Мы тестируем системы, работающие в Docker‑контейнерах, поэтому для юнит‑тестов тоже решили использовать контейнер. Таким образом, мы получили окружение, в котором будут как UT, так и целевая система.
В DockerHub есть несколько готовых контейнеров с SoapUI, но из соображений информационной безопасности, а также нашего удобства, мы решили создать свой.
«Голого» образа нам было недостаточно: нужно было подготовить окружения для соединения с тестируемыми системами, применить определенные настройки, синхронизировать версию из контейнера с версией, установленной у разработчиков. Мы выбрали промежуточный вариант между взятием готового и написанием с нуля: имитировали установку из дистрибутива.
Пример Docker-файла
FROM almalinux:8.9
ENV TZ=Europe/Moscow
RUN set -eux \
&& yum install -y fontconfig \
&& rm -rf /tmp/yum* ;
WORKDIR /usr/local/SmartBear
ARG SOAPUI_VERSION=5.7.0
ENV PROJECT_DIR ./project
ENV REPORTS_DIR ./reports
ENV MOUNTED_PROJECT_DIR ./project
ENV MOUNTED_EXT_DIR ./ext
ENV SOAPUI_DIR ./SoapUI-$SOAPUI_VERSION
COPY ./soapui/Files/* ./
RUN chmod 777 ./SoapUI-x64-$SOAPUI_VERSION.sh \
&& ./SoapUI-x64-$SOAPUI_VERSION.sh -q -dir $SOAPUI_DIR \
&& rm ./SoapUI-x64-$SOAPUI_VERSION.sh \
&& mkdir ./interfaces \
&& chmod 777 ./entrypoint.sh
COPY ./soapui/interfaces/* ./interfaces/
ENV JAVA_HOME $SOAPUI_DIR/jre/bin
ENV PATH $PATH:$JAVA_HOME
ENTRYPOINT ./entrypoint.sh
Пример entrypoint.sh
#!/bin/bash
echo "Applying custom settings"
cat ./soapui-log4j.xml > /usr/local/SmartBear/SoapUI-5.7.0/bin/soapui-log4j.xml
project_list=$(ls /usr/local/SmartBear/tests/*.xml)
exec_command="-f /usr/local/SmartBear/reports -I -j -A -t /usr/local/SmartBear/soapui-settings.xml"
properties_command=""
echo "Collecting properties"
while read -r prop; do
properties_command=$properties_command" -P ""$prop"
done < $PROPERTIES_PATH
for file_name in $project_list ; do
echo "Running tests "$file_name
running_command=$exec_command' '$file_name"$properties_command"
$SOAPUI_DIR/bin/testrunner.sh $running_command
done
Особое внимание нужно обратить на блок «Collecting properties». GUI SoapUI позволяет импортировать properties из файла, скрипт testrunner же требует, чтобы каждая такая настройка была передана отдельно с ключом ‑P.
При рефакторинге UT мы в первую очередь стандартизировали настройки: адрес для подключения к серверу, относительный путь хранения исходников на рабочем компьютере, endpoint и т. д. Собрали их в одном файле, чтобы было удобно работать как при автозапуске, так и вручную. Суммарно набралось около 100 пунктов.
Обратите внимание на символ конца строки в файле для хранения настроек: он должен быть LF. В противном случае может возникнуть похожая ошибка:
Пример ошибки при неправильном окончании строки
Нюансы работы с SoapUI-проектом через Python
Наши системы объемные и проектов с тестами набралось больше сотни. Исправлять их вручную не хотелось совсем.
Работа с SoapUI-проектом не отличается от работы с любым другим XML. Кратко рассмотрим создание и сохранение проекта с помощью Python и библиотеки lxml.
Новый SoapUI-проект можно создать следующим образом:
from lxml import objectify
from copy import deepcopy
# создание нового проекта
unittest_project = objectify.Element(
_tag='{http://eviware.com/soapui/config}soapui-project',
attrib={"activeEnvironment": "Default",
"name": f"UnitTests_{service_name}_{operation_name}",
"soapui-version": "5.7.0",
"abortOnError": "false",
"runType": "SEQUENTIAL"},
nsmap={"con": "http://eviware.com/soapui/config",
"xsi": "http://www.w3.org/2001/XMLSchema-instance"})
# вариант открытия существующего проекта
unittest_project = deepcopy(objectify.parse(unittest_path).getroot())
Корневой тег — это «soapui-project». Обратите внимание на nsmap и пространство имен «http://eviware.com/soapui/config»: префикс «con» будет использоваться почти во всех тегах проекта SoapUI.
Теперь сохраним проект:
objectify.deannotate(new_project, cleanup_namespaces=True)
obj_xml = etree.tostring(new_project, pretty_print=True)
with open(file=new_project_file_name, mode='w') as f:
f.write(obj_xml.decode('utf-8'))
При сохранении проекта рекомендую применять objectify.deannotate — он позволяет избавиться от артефактов обработки XML через Python, например, от префиксов pyval у тегов. Неправильно указанный неймспейс SoapUI обработать не сможет.
Послесловие
В этой части статьи я рассмотрела основные моменты работы с SoapUI, которые будут полезны для создания окружения для автоматизации запуска юнит‑тестов, а также для более удобной ручной отладки.
Во второй части мы продолжим углубляться в SoapUI и подробно разберемся в структуре его проектов.
Продолжение следует…