Всем привет, меня зовут Вероника Дюкарева. Я работаю старшим инженером‑программистом в компании 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:

  1. testrunner

  2. toolrunner

  3. loadtestrunner

  4. mockservicerunner

  5. 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
Пример аргументов командной строки для скрипта 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 и подробно разберемся в структуре его проектов.

Продолжение следует...

Комментарии (0)