В современных информационных системах значительная часть функциональности недоступна анонимным пользователям; трудно представить корпоративный портал или любую цифровую платформу, работа с которой не начиналась бы с нажатия кнопки входа в аккаунт. Процесс аутентификации при всей своей необходимости также привносит и дополнительные трудности для разработчиков и тестировщиков, среди которых, в частности, сложности при проведении нагрузочного тестирования. При разработке сценариев для нагрузочного тестирования мы неизбежно столкнемся с тем, что практически любой типовой профиль нагрузки предполагает использование закрытой части системы, куда без аутентификации попасть невозможно. Чтобы решить эту проблему, не нужно создавать отдельную ветку с измененной версией проекта, где все нужные страницы будут перенесены в публичный доступ. Необходимые инструменты есть и в самом JMeter, хоть они и чуть менее известны, чем стандартные компоненты тестового плана (вроде Thread Group), без которых этот план обойтись не может. В сегодняшней небольшой заметке мы рассмотрим, что можно сделать, чтобы обеспечить возможность прохождения аутентификации в тестовых сценариях при нагрузочном тестировании.
Итак, в нашем случае мы используем Keycloak в качестве провайдера аутентификации и авторизации (протокол OIDC). Для проведения нагрузочного тестирования, как уже понятно, используется стандартное решение Apache JMeter. Общий Test Plan, который мы используем, имеет следующий вид:

Мы предполагаем, что тестируемая система достаточно комплексная, вручную запросы к системе в тест план не добавляются, а используется “HTTP(S) Test Script Recorder” (возможно поговорим о нем подробнее в другой раз). Есть также “Switch Controller” для выбора тестового сценария и “Recording Controller” для группировки записываемых запросов по прецедентам. Количество потоков и пользователей не имеет значения в контексте данной заметки и остается на усмотрение читателя. Среди множества сценариев оставлены два типовых, один из которых не предполагает аутентификации (Use case № 1), а другой соответственно предполагает (Use case № 10). В данной статье мы сосредоточимся на втором.
Итак, мы настроили свой регистратор на запись запросов, указали нужный Target Controller и можем нажимать кнопку Start

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

Но если при записи запросов все пройдет хорошо, то при попытке запустить сценарий автоматически мы будем получать ошибки, поскольку доступ к закрытой функциональности будет запрещен. Это связано с тем, что после записи recorder’ом все параметры, необходимые для аутентификации, оказываются “захардкожены”, в то время как некоторые из них отличаются от запуска к запуску и отдаются кейклоком в результате обработки запроса на аутентификацию. Среди всех сгенерированных запросов нам надо найти два, касающихся аутентификации (рекомендуется для удобства также обозначить их в названии)

Первый из них запрос /auth. Здесь мы обращаемся по адресу кейклока (auth.example.com) к нужному реалму и передаем стандартные параметры, такие как client_id, адрес для редиректа на систему при успешной аутентификации и другие. Здесь мы также можем заменить некоторые параметры переменными, определенными в настройках “User Defined Variables” (к которым обращаемся через фигурные скобки) для большей гибкости и универсальности решения

Однако самую важную работу делают постпроцессоры “Boundary Extractor”, указанные в тест плане на предыдущей картинке. Мы используем их, чтобы достать из ответа от Keycloak все необходимые параметры для дальнейшей процедуры логина. Сделать это можно так, как указано ниже.
Первый постпроцессор:

Второй постпроцессор:

Третий постпроцессор:
Указанные значения сохранены в переменные, а значит теперь мы сможем использовать их как параметры в запросе аутентификации. Это второй запрос к auth.example.com, который нас интересует, путь которого должен быть
/realms/demo-realm/login-actions/authenticate?session_code=${session_code}&execution=${execution}&client_id=${client_id}&tab_id=${tab_id}
client_id в данном случае известная заранее переменная, заданная тестировщиком в “User Defined Variables”. Остальные параметры запроса получены постпроцессорами JMeter. Несмотря на то, что здесь мы представили запрос с переменными вместо конкретных параметров, стоит понимать, что сразу после записи сценария recorder’ом все эти параметры будут иметь статичное значение, которое они имели в момент логина при записи сценария. Заменив же фиксированные значения параметров, которые теряют актуальность при каждой новой процедуре аутентификации, на переменные, подхватываемые прямо из вновь пришедшего ответа в keycloak, мы смогли обеспечить воспроизводимость, необходимую для проведения тестирования.
После осуществления всех проделанных действий сценарий тестирования можно запустить, даже если он предполагает наличие доступа к закрытой функциональности, поскольку процесс больше не упирается в проблемы со входом в аккаунт.