[Из песочницы] Как протестировать приложение при взаимодействии с API с помощью SoapUI

Многие используют SoapUI для того, чтобы тестировать как сам API, так и приложения, обращающиеся к API. Довольно гибкий инструмент, позволяющий, например, экспортировать swagger файл API и сгенерировать Mock-service на его основе.

Не так давно у нас в компании я столкнулся с похожей задачей, но с нетривиальными условиями. Исходные данные: необходимо протестировать серверное приложение, которое получает на вход задачу, в процессе выполнения обращается к АПИ, каждый последующий запрос зависит от ответа АПИ. Логика вшита в приложение. То есть своеобразный черный ящик, где нужно протестировать множество выходов из сценария, когда вход в сценарий один.

image

Ниже я предлагаю пример решения, которое позволило достаточно просто встроить его в регрессионную инфраструктуру, а также имеющее запас для масштабирования, в случае увеличения покрытия диапазона сценариев и их сложности.

Для начала создаем Mock-service в SoapUI. Это делается в несколько кликов:

image

Теперь перейдем к созданию заглушек для запросов. Так как в поставленной задаче вход в сценарий один, у нас есть варианты:

  1. использовать идентификатор сценария и передавать его в каждом запросе, а в каждой заглушке определять ответ в зависимости от этого идентификатора;
  2. заранее указать список ответов для каждой заглушки и хранить их в глобальных переменных перед прогоном теста.


Первый вариант может использоваться в случае, когда необходимо получать ответ от заглушки одновременно для нескольких запросов. Реализация требует от клиентского приложения способности передавать определенный идентификатор в каждом запросе к АПИ. В нашем случае это было практически невозможно, а одновременное тестирования нескольких сценариев не требовалось, поэтому был выбран второй вариант.

Итак, для назначения списка ответов заглушек выполняем следующий запрос перед запуском теста:

Post: http://mockserver:8080/setscenario
Body: ScenarioId=0&Authentication=200_OK&AutoSystemHome=400_TokenIsMissing…


В Mock-сервисе добавляем обработку запроса «SetScenario». Он имеет всего два ответа:»200_OK» в случае корректной обработки входящего запроса, и »400_BadRequest» если запрос был составлен неверно:

image

В скрипте мы распределяем в глобальные переменные значения ответов для каждой заглушки:

def reqBody = mockRequest.getRequestContent() //считываем тело запроса
def reqBodyParams = [:]
reqBody.tokenize("&").each //собираем входные параметры, переданные в запросе
{
        param->
        def keyAndValue = param.split("=")
        reqBodyParams[keyAndValue[0]]=keyAndValue[1]
}
if (reqBodyParams.containsKey('ScenarioId')) // ID сценария как обязательный параметр (необходим для упрощения разбора логов); сюда можно добавить проверки на прочие обязательные для вас параметры запроса
{
// назначаем глобальные переменные, в которых будем хранить переданные значения ответов для заглушек, "?:” – означает какое дефолтное значение будет назначено в случае отсутствия переданного:
        context.mockService.setPropertyValue("ScenarioId", reqBodyParams["ScenarioId"] ?: "0")
        context.mockService.setPropertyValue("Authentication", reqBodyParams["Authentication"] ?: "200_OK")
        context.mockService.setPropertyValue("AutoSystemHome", reqBodyParams["AutoSystemHome"] ?: "200_OK")
        // и так далее для каждого метода …
        
        return "200_OK"
}
else 
{
        return "400_BadRequest"
}


Назначенные переменные можно увидеть в окне настроек сервиса:

image

Таким образом, мы описываем всю логику работы Mock-сервиса именно в этом методе, а в заглушках для методов АПИ достаточно написать скрипт, считывающий значение ответа из глобальной переменной:

image

Authentication = context.mockService.getPropertyValue("Authentication")
return "${Authentication}"


image

AutoSystemHome = context.mockService.getPropertyValue("AutoSystemHome")
return "${AutoSystemHome}"


Если необходимо добавить сценарии таймаутов, задержки в ответах, то добавляем переменную delay, к примеру:

Post: http://mockserver:8080/setscenario
Body: ScenarioId=0&Delay=600&Authentication=200_OK &AutoSystemHome=400_TokenIsMissing…


А в скрипт заглушки добавляем:

…
Authentication = context.mockService.getPropertyValue("Authentication")
Delay = context.mockService.getPropertyValue("Delay").toInteger()
sleep(Delay)
return "${Authentication}"


Если необходимо поддержать повторный запрос, то передаем список ответов:

Body: Authentication:400_MissingParametersClientId;400_MissingParametersClientId;200_OK


А в скрипте обработки добавляем tokenize и удаление уже отправленного ответа, в случае если он не последний:

def Authentication = []
Authentication = context.mockService.getPropertyValue("Authentication").tokenize("%3B")
if (Authentication.size() > 1)
{
        Authentication.remove(0)
        Authentication = Authentication.join("%3B")
        context.mockService.setPropertyValue("Authentication", Authentication)
}


Как итог мы получили простой Mock-сервис, который легко перемещать между тестовыми стендами и средами, ведь файл проекта — это xml-файл. Сервис запускается сразу после импортирования, без дополнительных настроек (за исключением изменения адреса сервера и порта, конечно). В данный момент он помогает нам тестировать приложение независимо от стабильности АПИ сервера и возможных временных периодов его недоступности.

Что планируем добавить: интегрировать это решение для тестирования приложений до и во время разработки самого АПИ. Например, когда уже готово его описание в виде swagger-файла, но сервер в процессе настройки. Циклы разработки АПИ и клиентских приложений не всегда совпадают. В этот момент полезно на чем-то тестировать разрабатываемое клиентское приложение, и Mock-сервис может сильно помочь.

UPD: в случае если появятся вопросы и полезные замечания.

© Habrahabr.ru