В этой статье я хочу поделиться опытом освоения тестирования (в т. ч. автоматизации) на уровне API (Application Programming Interface – интерфейс программирования приложений, интерфейс прикладного программирования). Надеюсь, что предлагаемый материал будет представлять интерес для всех, кто ранее проводил тестирование через графический интерфейс и еще не имеет опыта работы с http-запросами. Или можно обратиться к нашим опытным специалистам.
Немного о REST API и SOAP API
Стоит отметить, что на сегодняшний день есть два основных подхода к построению программного интерфейса веб-приложений: REST (RESTful) API и SOAP API:
- REST (от англ. Representational State Transfer – «передача состояния представления») обеспечивает общение между клиентом (как правило, это браузер) и сервером с помощью обычных HTTP-запросов (GET, POST, PUT, DELETE и т. д), передавая информацию от клиента в параметрах самих запросов, информацию от сервера – в теле ответа (который может быть, например, JSON-объектом или XML-документом). REST является архитектурным стилем, а не стандартом.
- SOAP (от англ. Simple Object Access Protocol – простой протокол доступа к объектам, вплоть до спецификации 1.2) характеризуется использованием HTTP(S)-протокола лишь как транспорта (чаще всего, методом POST). Все детали сообщений (в обе стороны – от клиента к серверу и обратно) передаются в стандартизованном XML-документе. SOAP может работать и с другими протоколами прикладного уровня (SMTP, FTP), но чаще всего он применяется поверх HTTP(S). SOAP является протоколом и имеет спецификацию.
Если уподобить HTTP-запрос бумажному носителю, то можно сказать, что REST API в большинстве случаев передает простые записки, а время от времени – письмо в конверте (возможно, написав при этом часть послания и на самом конверте). В свою очередь, SOAP API передает все инструкции в подробном письме стандартного вида, используя конверт (единичный HTTP-запрос) лишь как средство доставки. Для лучшего понимания разницы подходов я рекомендую читателю посмотреть следующую инфографику.
В статье речь будет идти о REST API, так как этот подход является более распространенным из-за своей относительной простоты и удобства для разработчиков. SOAP API преимущественно характерен для больших корпоративных (enterprise) систем.
Чем работа с API может быть полезна тестировщику?
Для начала постараемся понять, зачем вообще тестировщику осваивать что-либо на таком уровне. Казалось бы, программные интерфейсы – это территория разработчиков. Ниже мы рассмотрим выгоды использования тестирования API.
Выгода первая: точная локализация.
Умение работать с API позволяет лучше понимать и точнее описывать возникшие ошибки. Предположим, вы открываете некую страницу сайта и видите в пользовательском интерфейсе сообщение об ошибке. Чем конкретно она вызвана? Насколько она серьезна? Может ли она отразиться на других частях системы? Нам будет гораздо проще ответить на эти вопросы, если мы сможем понять, какую именно функцию не удалось выполнить системе. Сообщения в графическом интерфейсе не всегда позволяют оценить эту зависимость.
Выгода вторая: экономия времени при подготовке тестовых данных и ситуаций.
Многие тесты требуют подготовки условий для их проведения. Так, для прохождения кейса про редактирование документа нам может потребоваться создать сам документ. В графическом интерфейсе это займет немало времени: придется, например, нажимать множество кнопок, чекбоксов, заполнять текстовые поля и т. д. По сути, мы выполняем лишь подготовительные действия для того, чтобы браузер отправил серверу запрос на создание документа. При этом сама отправка и исполнение запроса, как правило, занимает доли секунды.
Более того – многочисленные действия в браузере часто являются причиной ложных «падений» автоматизированных тестов, которым на уровне GUI свойственна «хрупкость». Одно неуспешное нажатие кнопки может привести к необходимости повторения либо всего теста, либо какой-то его части. Сформировав запрос программно или воспроизведя его с помощью специальных инструментов (об этом чуть позже), мы можем существенно сократить время проверки.
Выгода третья: возможность воспроизводить тесты на больших наборах входных данных.
Для некоторых проектов важно проводить тесты с большим количеством разнообразных наборов входных данных, отделенных от кода самого теста и вынесенных в отдельный файл. В этом случае один и тот же сценарий может повторяться многократно для разных значений. Эту методологию (так называемую Data Driven Testing) сложно реализовать через графический интерфейс с приемлемой скоростью и стабильностью. Напротив, на уровне http-запросов это делается очень быстро и с гораздо более высокой надежностью.
Выгода четвертая: возможность участвовать в проектах, где работа с API является требованием тест-дизайна.
Сам тест-дизайн проекта может оказаться таковым, что некоторая часть тестов должна будет проводиться только через программные интерфейсы (например, если на проекте реализована концепция «пирамиды тестирования», при которой лишь немногие тесты используют графический интерфейс). Также стоит отметить, что все большую популярность набирает архитектура микросервисов, когда приложение является не «монолитным», а фактически представляет собой набор самостоятельных приложений, обменивающихся данными. И в этом случае тестирование API становится особенно востребованным, так как именно оно позволяет в полной мере убедиться в правильности интеграции отдельных сервисов и в соблюдении логики функционирования системы в целом. Неумение работать с программными интерфейсами существенно ограничивает возможность вашего участия в таких проектах.
Практические шаги к освоению работы с REST API сайта
Итак, вы решили освоить работу с программным интерфейсом вашего сайта. С чего же начать?
Шаг 1: откройте средства разработчика в браузере.
Откройте средства разработчика (developer tools) в браузере (например, в Mozilla Firefox и Google Chrome просто нажмите F12) и перейдите во вкладку, в которой отражается сетевая активность (Network, Net, Сеть и т. д.)
Совершайте обычные действия в браузере и наблюдайте за результатом. Первое, что бросается в глаза, – это огромное количество запросов даже при загрузке одной страницы: для получения каждого изображения, стилей, шрифтов, структуры страницы, скриптов. При использовании AJAX (от англ. Asynchronous Javascript and XML – асинхронный JavaScript и XML, подход к построению интерактивных пользовательских интерфейсов веб-приложений, заключающийся в «фоновом» обмене данными браузера с веб-сервером) количество запросов может увеличиваться, даже если вы не производите никаких активных действий.
Шаг 2: скройте запросы, не относящиеся к логике работы.
Что же делать с огромным количеством запросов? Как выбрать те, которые для нас полезны? Попробуйте применить текстовый фильтр. Многие запросы, относящиеся к логике взаимодействия клиента и сервера (то есть, к API), могут содержать в своих URL фрагмент, указывающий на это (например, «api/rest»). Если вы обнаружили такой фрагмент – введите его в поисковую строку DevTools. Средства разработчика в браузере также позволяют выделить запросы определенного типа: XHR, JS, CSS, Images и т. д.
Выберите XHR (XMLHttpRequest) – это интерфейс языка JavaScript, используемый для конструирования запросов, имеющих тело. Обычно именно этот интерфейс используется для обращения к API. Вы можете столкнуться с большим количеством однотипных запросов, порождаемых различными системами мониторинга (например, yandex.webvisor). Их можно скрыть, применив негативный фильтр (например, «-yandex» для Chrome), даже если вы не знаете точно, что именно эти запросы делают. Однотипные и часто повторяющиеся запросы, как правило, относятся к мониторингу сайта, а не к логике совершаемых пользователем действий.
По клику на картинку откроется полная версия.
Просмотрите внимательно оставшиеся после применения фильтра запросы и постарайтесь выявить те, которые относятся именно к логике действий. В случае необходимости обратитесь за разъяснениями к разработчикам. Если вам доступна документация, в которой описываются endpoint-ы сервисов вашего проекта (т. е. адреса, к которым обращаются запросы, относящиеся к API), – изучите ее. Ниже я буду рассматривать вариант, когда подробной документации или соответствующих доступов у вас нет.
Достаточно часто по адресу endpoint-а можно догадаться, за что именно отвечает запрос к данному сервису. Например, адрес, содержащий фрагмент «api/rest/createContract», скорее всего, используется для создания договора, «api/rest/buildInfo» – для вывода информации о версии, установленной в текущий момент, а «api/rest/news/search» – для поиска новостей. Если трудно «выловить» нужный запрос в общей массе (а запросов к API тоже может быть немало) – очистите историю запросов перед совершением интересующего вас действия. Довольно быстро вы научитесь видеть нужные запросы и сопоставлять их с действиями в пользовательском интерфейсе.
Шаг 3: проведите более детальный анализ структуры запросов.
Обратите внимание на то, что часть запросов передает информацию только в запрошенном URL-е. В первую очередь, это GET-запросы, которые предназначены для получения определенного содержимого или запуска процесса. Классический пример – запрос на текстовый поиск. Его URL может выглядеть как «https://anysite.ru/search?keywords=my_query&itemsperpage=20», где «../search» – адрес поискового сервиса, «keywords=…» – текст запроса, «itemsperpage» – параметр, отвечающий за количество результатов, выводимых на одной странице.
С другой стороны, встречаются запросы, передающие большинство параметров в теле самого запроса. При этом часть параметров может передаваться и в URL. Примером могут послужить POST-запросы:
URL: POST https://www.youtube.com/feed_ajax?action_pause_watch_history=1 HTTP/1.1
Request headers: дополнительная информация, в т. ч. cookies.
Message Body:
session_token=QUFFLUhqbER0TE5idlp2MngybFh1ZUUyblYtaGlmLVhmZ3xBQ3Jtc0tta1J5W
m9uZHpVTW9oWHIzOWdIblkzVk1wVTNFQS1aS0pfNG85OWw3Sk14U2
В этом случае суть необходимого действия передается в URL-е (action_pause_watch_history=1), а информация о пользователе – в теле запроса (session_token).
На структуру тела запросов и ответов не накладывается ограничений. Это может быть как просто набор пар поле/значение (т. н. Form Data, как в примере выше session_token/значение), так и документ в удобном для разработчика формате (JSON, XML или каком-то ином).
Проанализируйте приходящие ответы (вкладка Response) и их коды. Помимо всего прочего, коды ответов, как правило, несут полезную информацию и сообщают о логике происходящего. Большинство запросов имеют код ответа «200 OK», сообщающий о том, что операция выполнена успешно. В случае возникновения ошибки коды будут начинаться на 4 (ошибка на стороне клиента) или на 5 (ошибка на стороне сервера). Например, таковы всем известные ошибки 404 («клиент запросил несуществующий ресурс») и 500 («внутренняя ошибка сервера»). Обратите внимание, что браузеры предоставляют возможность просмотра подробностей запросов/ответов как в удобном формате («parsed» в Google Chrome, «pretty print» в Mozilla Firefoх), так и в «сыром» виде («source»). Конечно, для понимания проще «parsed»/«pretty print», но в том случае, когда вам необходимо скопировать часть запроса, лучше переключиться в режим «source».
По клику на картинку откроется полная версия.
По клику на картинку откроется полная версия.
Шаг 4: воспроизведите запросы.
Итак, вы видите нужные запросы и понимаете, какие именно параметры они передают и с какой целью. Теперь можно попробовать самостоятельно менять значения параметров для получения нужных действий. Каким образом можно воспроизвести запросы с измененными параметрами? Самый простой способ — создавать GET-запросы, просто вводя их в адресную строку браузера (по сути, адресная строка – это интерфейс командной строки для воспроизведения GET-запросов). Но вам не удастся сделать многое, ограничившись лишь GET-методом.
Для расширения ваших возможностей используйте Fiddler или подобные ему инструменты (например, такие). Эти программы перехватывают весь сетевой трафик, позволяя просматривать, редактировать и воспроизводить отдельные запросы. Уже на этом уровне можно что-то тестировать – например, валидацию данных на стороне сервера. Если веб-клиент в браузере не позволил вам ввести некоторые значения – в Fiddler-е вы сконструируете запрос сами. Такой способ может существенно ускорить проверку большого набора данных для ввода, особенно если изменение значений в браузере занимает длительное время.
В качестве приятного бонуса я приведу небольшую пошаговую инструкцию по воспроизведению запроса в Fiddler.
- Настройте фильтрацию во вкладке Filters. Фильтрация возможна по множеству параметров, но мне представляется, что один из самых удобных вариантов – отображать запросы только для определенного сайта (хоста) и только при условии, что адрес запроса содержит определенный фрагмент (т. е. имеет отношение к API).По клику на картинку откроется полная версия.
- Кликните на интересующий вас запрос и перейдите во вкладку Inspectors. В верхней части выберите вариант Raw – вы увидите, каким образом запрос отображается в «сыром» виде. Скопируйте текст запроса.По клику на картинку откроется полная версия.
- Перейдите во вкладку Composer, выберите в ней Scratchpad, вставьте текст запроса.
- Отредактируйте нужные параметры, выделите текст запроса целиком (это обязательно) и нажмите Execute. Наблюдайте результат выполнения запроса в главном окне программы (убедитесь, что запрос в случае изменения параметров проходит фильтры).
По клику на картинку откроется полная версия.
Дальше – автоматизация тестирования REST API!
Постигнув принципы работы API, вы можете использовать эти навыки в автоматизированных тестах. Остается выбрать инструменты, которые будут воспроизводить нужные вам запросы и отслеживать содержимое ответов. Если вы умеете писать автоматизированные тесты для графического интерфейса (например, с использованием Selenium), то идеальным вариантом, на мой взгляд, будет интеграция тестов API в существующий фреймворк. Тесты часто содержат подготовительные/вспомогательные действия, многие из которых удобнее выполнить с использованием API. Это будет быстрее и стабильнее. Мы можем автоматизировать ваши тесты.
С другой стороны, механизм авторизации бывает достаточно сложным, его не всегда легко пройти только с помощью запросов. Но и это не представляет проблемы в том случае, если API-тесты интегрированы с тестами GUI. Нужные cookie можно забрать в браузере, открытом с помощью Selenium (driver.manage().getCookieNamed(«sessionId»).getValue()).
Для доступа к API посредством языка программирования потребуется библиотека, работающая с http-запросами. Для Java могу порекомендовать rest-assured, в своем фреймворке я использую именно ее. Эта библиотека удобна и популярна – у вас не возникает проблем ни с пониманием кода, ни с нахождением документации, примеров и обсуждений. Rest-assured использует синтаксис в стиле BDD (Behavior Driven Development – подход к автоматизации тестирования, стремящийся описывать код тестов языком, близким к естественному) с характерными ключевыми словами given/when/then:
protected ExtractableResponse postRequest(String requestPayload, String endpoint, int expectedStatus){
return
given().
auth().basic("login", "password").
cookies("sessionId", session).
contentType(ContentType.JSON).
body(requestPayload).
relaxedHTTPSValidation().
when().
post(endpoint).
then().
statusCode(expectedStatus).
body(containsString("www")).
extract();
}
Блок given описывает предусловия, такие как авторизация и параметры запроса. Обратите внимание: отдельно можно провести базовую http-авторизацию (auth().basic(«login», «password»)) и пользовательскую авторизацию, передав нужные куки (cookies(«sessionId», session)). Очень удобно использовать опцию relaxedHTTPSValidation(), чтобы избежать хлопот с подтверждением сертификатов. Блок when() описывает требуемое действие – запрос какого типа и на какой адрес следует отправить. Блок then() включает проверки, которые необходимо произвести (их может быть несколько одновременно).
Например, вы можете проверить, соответствует ли статус ответа ожидаемому (statusCode(expectedStatus)) и содержит ли тело ответа определенный фрагмент текста (body(containsString(«www»))). Команда extract() позволяет получить ответ и использовать его, например, для извлечения определенных значений.
Это можно сделать следующим образом:
ExtractableResponse response = postRequest(session, payload, endpoint, 200);
int numberOfResults = response.path("results.total");
Здесь команда path(«results.total») позволяет извлечь значение, используя JsonPath либо XPath (в зависимости от того, в каком формате предоставлен ответ). Обратите внимание, что вызываемый метод postRequest определен в моем фреймворке, а не в библиотеке rest-assured. Если вам важно просто получить тело ответа для дальнейших проверок, а блок then не изменяется, то удобнее всего будет для каждого типа запросов (get, post, delete и т. д.) создать свой вспомогательный метод и не прописывать given/when/then каждый раз. Для работы с языком Python используется популярная библиотека requests.
Стоит отметить, что и Java, и Python имеют средства работы с http в числе стандартных возможностей, но я неоднократно сталкивался с мнением, что они не слишком удобны. Если вы не планируете писать тесты с использованием языка программирования, то вам, скорее всего, подойдет инструмент Postman – он позволяет воспроизводить и сохранять запросы, а также выстраивать из них тесты.
Заключение
Все сказанное выше позволяет сделать следующий вывод: умение тестировщиков работать с API может принести пользу практически любому проекту. Например, это дает возможность в считанные минуты провести смоук (и убедиться, что ничего важного не сломалось), выполнить одни и те же тесты с разнообразными наборами входных данных или быстро проделать какие-либо вспомогательные действия по созданию тестовых данных и ситуаций.
Наконец, еще раз хочу напомнить, что тестирование API становится особенно востребованным в свете растущей популярности микросервисной архитектуры. Следовательно, даже в том случае, если на вашем проекте пока не используется тестирование на уровне API, вам имеет смысл присмотреться к возможностям, которые оно предоставляет.