
Привет! Я Аня, QA Engineer в KODE. В этой статье расскажу, как мы используем локальный запуск бэкенда в QA, в каких сценариях он помогает и когда без него вполне можно обойтись.
В обычной работе QA тестовый стенд закрывает большую часть задач. Нам его стало не хватать, когда мы начали разбирать ошибки с прода: часть проблем не воспроизводилась на dev, а в Grafana не всегда было видно, что именно происходило во внутренних вызовах и запросах к интеграторам.
В какой-то момент мы попробовали поднимать Java-бэкенд локально. И оказалось, что это сильно меняет само расследование дефектов. Тестировщик может не только увидеть итоговую ошибку, но и пройти по цепочке запросов, получить cURL обращения к интегратору, подменить его ответ или проверить фикс на тех данных, на которых возникла проблема.
Почему тестового стенда недостаточно
Пока мы проверяем обычный функциональный сценарий, локальный запуск чаще всего не нужен. Отправили запрос на dev, получили ответ, сверили результат с требованиями.
Сложности начинаются при расследовании дефекта. Здесь итогового ответа API уже мало: нужно понять, на каком именно этапе что-то пошло не так.
Один входящий запрос может запускать внутри бэкенда несколько операций: сервис получает данные, обращается во внешнюю систему, обрабатывает ее ответ, делает следующий запрос и только после этого формирует ответ клиенту.
Если на каком-то этапе возникает ошибка, снаружи мы можем увидеть только общий результат, например 500.
В нашем случае основным источником информации были продовые логи в Grafana. Но логирование на production ограничено. В частности, в логах нет полных cURL-запросов к внешним интеграторам.
Поэтому при расследовании не всегда удавалось быстро понять:
- какой именно внутренний запрос завершился ошибкой;
- какие данные наш сервис передал интегратору;
- что произошло на стороне внешней системы;
- проблема находится у нас или у интегратора;
- можно ли воспроизвести конкретный запрос отдельно.
Еще одна проблема — данные. Иногда дефект возникает только при определенном состоянии конкретной сущности на проде. На dev таких данных нет, и повторить тот же сценарий просто не получается. В таких ситуациях тестовый стенд продолжает выполнять свою задачу, но для расследования его возможностей уже недостаточно.
Зачем QA вообще запускать Java-бэкенд локально
Причина, почему QA редко это делает, довольно банальная: обычно в этом просто нет необходимости. Есть dev-стенд, endpoint, Postman — работаешь с ними. Поэтому локальный запуск чаще остается территорией разработчиков.
Плюс со стороны может показаться, что для этого нужно хорошо знать Java и разбираться во внутреннем устройстве приложения.
Но писать код на Java не понадобилось. Задача была другой: запустить существующий сервис, направить в него нужный запрос и посмотреть, что происходит дальше.
Из навыков пригодились:
- работа с Git;
- понимание REST API и HTTP;
- Postman;
- базовое понимание структуры Java-проекта;
- работа с конфигурацией и переменными окружения;
- чтение логов;
- понимание взаимодействия сервиса с внешними системами;
- базовое знание Docker там, где он используется.
Идея попробовать локальный запуск появилась во время расследования сложных дефектов. В какой-то момент стало быстрее запустить сервис и посмотреть цепочку выполнения напрямую, чем пытаться восстановить ее по логам или каждый раз подключать разработчика.
На первую настройку ушло около пары часов: нужно было установить зависимости, разобраться с конфигурацией и подключением необходимых сервисов. После этого локальный запуск уже не требовал отдельной подготовки под каждое расследование.
Как изменилось расследование ошибок с прода
Раньше процесс начинался с Grafana. Находим запрос, изучаем доступные логи и пытаемся понять, где произошла ошибка. Если информации хватало, проблему можно было локализовать сразу. Если нет, то начинался ресерч: попытки воспроизвести сценарий на dev, поиск подходящих данных, восстановление запросов к интегратору, а затем подключение backend-разработчика. Причем время уходило не только на поиск причины ошибки. Много занимал именно сбор фактуры вокруг нее.
После появления локального запуска процесс стал другим. И теперь сценарий выглядит так: 1. Находим запрос, который привел к ошибке на проде.2. Воспроизводим его через локально запущенный сервис. 3. Смотрим цепочку внутренних вызовов.4. Определяем подзапрос, на котором возникает ошибка.5. При необходимости получаем cURL обращения к интегратору.6. Повторяем этот запрос отдельно.
Это позволяет довольно быстро разделить две ситуации. Например, тот же cURL напрямую к интегратору тоже падает — значит, у нас уже есть конкретная фактура для внешней команды. Или интегратор отвечает корректно, а ошибка появляется позже при обработке ответа нашим сервисом, тогда понятно, куда копать внутри проекта. Особенно полезна здесь возможность получить cURL.
На проде мы можем видеть сам факт ошибки, но не иметь полного запроса, который backend отправил во внешнюю систему. Локально этот запрос можно получить и повторить отдельно от всей цепочки.
В итоге вместо «При таком сценарии падает интеграция», можно передать значительно более конкретную информацию: какой запрос отправили, какой ответ получили и после какого шага возникла ошибка.
И вот здесь для нас оказался важен не сам факт локального запуска, а изменение роли QA в расследовании. Раньше при недостатке информации приходилось подключать разработчика уже на этапе первичного ресерча. Теперь значительную часть фактуры тестировщик может собрать сам.
Разработчик подключается, когда действительно нужен анализ кода или исправление проблемы, а не только для того, чтобы достать информацию, которой QA не видит в Grafana.
Как проверить ответ интегратора, который он никогда не присылает на тесте
Вторая полезная задача локального запуска — это негативные сценарии интеграций. При работе с реальной внешней системой мы зависим от того, какой ответ она возвращает сейчас. Допустим, нужно проверить, как backend поведет себя, если интегратор:
- вернет null вместо ожидаемого значения;
- вообще не передаст обязательное поле;
- пришлет JSON другой структуры;
- ответит 400, 404 или 500;
- вернет какой-то другой нестандартный ответ.
Получить нужное поведение от настоящего интегратора специально ради проверки бывает либо сложно, либо вообще невозможно.
Для таких сценариев мы используем Mock Server в Postman. В конфигурации локального сервиса вместо реального интегратора указываем mock и сами задаем ответ, который хотим проверить.
И смотрим уже не на интегратора, а на наш backend: что произойдет с запросом, как будет обработано пустое значение, не закончится ли все внутренней ошибкой.
Тем же способом можно удалить поле целиком, поменять структуру JSON или вернуть нужный HTTP-код.
На реальном интеграторе такой набор негативных сценариев воспроизвести по запросу не получится. С mock мы можем последовательно проверить каждый из них и посмотреть, насколько устойчиво наш сервис ведет себя при неожиданных ответах внешней системы.
Это полезно и при проверке конкретного исправления: если баг возник из-за определенного ответа интегратора, через Mock Server можно зафиксировать именно этот ответ и повторять сценарий столько раз, сколько нужно.
Как проверить фикс, если на dev нет нужных данных
Отдельная боль при расследовании продовых дефектов — данные. Например, дефект возник на проде, разработчик нашел причину и подготовил исправление. Но на dev нет сущности в том же состоянии, а создать ее вручную либо сложно, либо вообще невозможно. Получается, что фикс есть, но проверить именно исходный проблемный сценарий не на чем.
Локальный запуск дает дополнительный вариант. Если архитектура проекта и доступы это позволяют, локальную версию сервиса можно подключить к нужному окружению и выполнить сценарий на тех данных, на которых возникла ошибка.
Получается связка: исправленный код локально + данные, на которых воспроизводился дефект.
Так можно проверить, действительно ли изменение закрывает исходную проблему, еще до доставки версии выше по цепочке окружений.
Это не заменяет дальнейшее тестирование фикса на стенде. Скорее дает возможность раньше проверить самый важный сценарий, тот, ради которого изменение вообще делали.
Где такой подход особенно полезен
После нескольких таких расследований стало понятно, что локальный backend нужен для вполне определенного набора задач.
В первую очередь он полезен при работе с интеграциями. Если один запрос внутри сервиса порождает несколько обращений во внешние системы, возможность посмотреть конкретные вызовы заметно упрощает ресерч.Второй случай — дефекты, завязанные на данные, которые сложно воспроизвести на dev. Третий — негативные сценарии внешних систем, которые удобно моделировать через Mock Server.И четвертый — ситуации, когда QA регулярно приходится звать backend-разработчика не потому, что нужен его анализ кода, а потому, что у тестировщика просто нет доступа к нужной технической информации. В этом случае локальный запуск позволяет убрать лишний этап из расследования.
Когда поднимать сервис локально не нужно
Запускать backend локально ради каждой ошибки точно не нужно.
Если дефект стабильно воспроизводится на dev, нужные данные есть, а Grafana показывает достаточно информации, проще продолжить работать на тестовом стенде.
Локальное окружение к тому же требует поддержки. Проект развивается, меняются зависимости, конфигурация и переменные окружения. Если сервис запускают раз в полгода, вполне может оказаться, что в нужный момент старая инструкция уже не работает.
Есть и более существенное ограничение: локальная среда не является точной копией production. Могут отличаться конфигурация, версии связанных сервисов, данные и сетевые условия. Поэтому далеко не любую продовую проблему вообще имеет смысл пытаться воспроизводить локально. Например, такой подход мало поможет, если причина связана с инфраструктурой, нагрузкой или особенностями конкретного окружения.
Отдельный вопрос — реальные данные. Если локальный сервис подключается к окружению с чувствительными или близкими к production данными, необходимо соблюдать принятые на проекте правила доступа и безопасности. Локальный запуск — дополнительный инструмент для исследования, а не способ обойти ограничения между окружениями. Поэтому для части сценариев безопаснее и проще использовать mock или специально подготовленные тестовые данные.
Как понять, нужен ли локальный запуск на вашем проекте
Мы бы не вводили локальный backend как обязательную практику для всех тестировщиков. Проще посмотреть, какие задачи регулярно возникают на конкретном проекте. Попробовать подход имеет смысл, если:
- ошибки появляются на проде, но не воспроизводятся на dev;
- доступных логов регулярно не хватает для расследования;
- у сервиса много внешних интеграций;
- QA необходимо видеть конкретные внутренние запросы;
- нужно проверять нестандартные ответы интеграторов;
- сценарии сильно зависят от данных;
- backend-разработчики регулярно подключаются к первичному ресерчу только из-за нехватки информации у QA.
Если таких ситуаций почти нет, отдельная настройка локального окружения, скорее всего, не окупится. Если это постоянная часть работы, то инструмент уже выглядит гораздо полезнее. Для нас главным критерием стал простой вопрос: получим ли мы локально информацию или контроль над сценарием, которых сейчас не дает тестовый стенд? Если да, локальный запуск имеет смысл.
Локальный backend не заменил нам dev-стенд, Grafana или Postman. Он пригодился именно там, где этих инструментов переставало хватать для расследования.
QA может воспроизвести запрос с прода, пройти по внутренней цепочке вызовов, получить cURL к интегратору, подменить ответ внешней системы через Mock Server или проверить исправление на проблемных данных.
Для сложного интеграционного проекта это, пожалуй, и есть главный плюс локального запуска: разработчик подключается уже к самой проблеме, а не начинает расследование с нуля.

Люблю такие разборы: без громких обещаний, зато с понятной логикой.
Хороший пример того, как маленькие решения влияют на итоговый результат.