Привет, Хабр! Я Святослав Волохов, QA-инженер в AGIMA. Тот, кто хоть раз тестировал сложный интеграционный сервис с десятками зависимостей и внешними API, да еще и в финтехе, знает — без грамотно выстроенной системы тестирования не обойтись.

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

Расскажу о сложностях, с которыми мы столкнулись, и об их решениях — берите на заметку, особенно если работаете над enterprise-проектами и вы — начинающий специалист.

Немного контекста: что за проект

Проект, о котором пойдет речь, представляет собой мультисервисную экосистему для ведения финансовых операций и управления продуктами. Любой пользователь здесь может открыть вклад, взять кредит, купить КАСКО и ОСАГО, застраховать квартиру и многое другое.

Мы участвуем в разработке ключевых модулей: от проектирования логики до продвинутого фронтенда. Причем как в мобильном приложении, так и в веб-версии.

Вызов №1: внешние сервисы без тестовых контуров

Особенность проекта в том, что это не замкнутая система — она тесно интегрирована с внешними провайдерами. Мы каждый день ходим в десятки внешних API: брокеры, страховые, маркетплейсы, поставщики данных по недвижимости и автомобилям. Но далеко не все из них обеспечивают тестовую среду. У кого-то просто нет среды, у других — платные вызовы, а кто-то отдает нестабильные данные.

Например, модуль по автострахованию использует данные по VIN-кодам. Это внешняя интеграция, которая требует платных запросов. Один запрос — одна стоимость. При этом на ручное и нагрузочное тестирование нужны десятки или сотни вызовов. Получается, что каждое изменение приходится проверять «за деньги».

В другом кейсе, связанном с анализом активов пользователя, один из партнеров не имел стабильного тестового окружения. Сначала API был доступен, потом — пропадал. При этом его поведение менялось. Мы не могли положиться на него для автотестов.

Решение: кастомные моки

Вместо попыток договориться с каждым партнером о тестовом окружении (спойлер: это почти невозможно), мы пошли другим путем — написали свой мок-сервис на основе WireMock. Он не интегрирован напрямую в pipeline CI/CD — это осознанное решение. Внутри пайплайна тесты просто направляются на нужный стенд, где моки уже подняты и готовы к работе. Это проще, быстрее и гибче для нашей инфраструктуры.

Как работает наш мок-сервис:

  1. Тестировщик формирует мок и отправляет его через HTTP-запрос в базу данных.

  2. Мы подменяем URL вызова в нужном функционале.

  3. Когда вызывается этот метод, мок-сервис перехватывает запрос и возвращает нужный ответ из БД — в зависимости от метода, параметров и тела запроса.

Мы специально не использовали готовый WireMock, потому что хотели полный контроль и гибкость. Наш сервис кастомный, конфигурируется через Swagger, и адаптирован под особенности нашей экосистемы. Благодаря этому тестировать стало проще даже новичкам — не нужно разбираться в чужом пайплайне или инфраструктуре.

Вместо двух часов ручной подготовки данных в соседней команде — 20 минут с моком, и ты уже симулировал нужную 500-ку.

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

  • STRICT — точное совпадение запроса по URL, параметрам и телу;

  • BODY_PRESENT — достаточно валидного JSON в теле;

  • URL_ONLY — подмена по URL независимо от тела и параметров.

Если мок не находится — запрос проксируется в реальный сервис. Есть даже поддержка прокси-хедеров для авторизации. А самое главное — всё это лежит на наших стендах и контролируется нами.

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

Вот какие плюсы это нам дало:

  • Возможность проводить тестирование, даже если внешнего сервиса нет или он «ложится».

  • Эмуляция ответов сервиса, как положительных так и ошибочных.

  • Защита от нестабильных данных и непредсказуемого поведения.

  • Снижение затрат (финансовых и временных) при нагрузочном и регрессионном тестировании.

А как мы обеспечиваем актуальность моков при изменениях у партнеров? 

Здесь всё держится на трех опорах: коммуникации, ревью и интеграционных тестах. Если команда вносит изменения, которые могут повлиять на другие модули, они предупреждают коллег. Кроме того, такие изменения проходят через code review, где можно заметить возможные эффекты. И если всё же что-то проскочило — мы поймаем это на продлайке, когда отключаем моки и проверяем живую интеграцию.

Вызов №2: дорогие вызовы и лимиты

Как я писал выше, у некоторых поставщиков данных — особенно в блоке недвижимости и автомобилей — вызовы к API стоят денег или даются в ограниченном объеме. Например, 150 запросов в месяц. С учетом автотестов, ручных прогонов и нагрузочных тестов — это почти ничего.

Что делать в таких условиях:

  • Во-первых, мы изолировали командный стенд. Все обращения к внешним API на нем блокируются и заменяются моками.

  • Во-вторых, мы на продлайке мы отключаем моки и проверяем, что всё работает с реальными партнерами.

Такой подход позволяет нам:

  • не тратить вызовы вхолостую;

  • получать стабильные ответы для автотестов;

  • точно воспроизводить ошибки и баги.

Один из реальных кейсов — API начал возвращать названия автомобилей в верхнем регистре, например HYUNDAI вместо Hyundai. Наш парсер не был к этому готов и начал падать. Если бы мы заранее эмулировали этот сценарий моками, можно было бы избежать проблем на проде.

Такие истории убедили нас: чем больше нестандартных ситуаций мы можем протестировать заранее — тем лучше. Сейчас мы добавили:

  • положительный сценарий;

  • отрицательный (неизвестная марка);

  • корнер-кейс (некорректный формат);

  • ошибки (500, 403, пустой ответ).

Вызов №3: интеграционные тесты должны быть стабильными

Наш проект состоит из десятков микросервисов, объединенных в единую экосистему. Одно падение может блокировать релиз. Поэтому интеграционные тесты — это не формальность, а механизм защиты. Если они «лежат» — значит, весь пайплайн тормозит.

Невалидные ответы, сломанные схемы, неожиданные тайм-ауты — всё это может разрушить цепочку. А если у вас тест не проходит из-за стороннего сервиса, который вы не можете контролировать — это превращается в блокер. Моки помогают этого избежать. Мы можем изолировать систему, убедиться, что наш код стабилен, и двигаться дальше.

Кроме того, автоматизация и CI/CD требуют стабильной среды. Мы не можем позволить себе, чтобы тесты «временно не работали». Это замедляет всё. Моки, используемые на командном стенде, позволяют не зависеть от партнеров и экономят массу времени.

Вызов №4: командная коммуникация

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

  • Горизонтальные связи: тестировщики, фронты, аналитики разных команд коммуницируют между собой.

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

  • Отдельные гильдии: тестирование, Android, iOS, бэкенд.

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

Как устроена совместная работа над интеграционными тестами

Большая часть наших тестов — интеграционные. Это и плюс, и боль: мы сразу видим, где сломалась интеграция, но нестабильность соседних сервисов может мешать.

У нас есть общие тестовые фреймворки, общие BFR, и вызовы эмулируются на уровне клиента (веб или мобилка). Например, чтобы проверить диверсификацию в одном из модулей, нужно сначала добавить вклады, пифы, облигации — которые принадлежат другим модулям. Если один из них не работает, мы видим это сразу. Интеграционные тесты показывают, где в экосистеме что-то пошло не так.

У нас также настроены проверки совместимости и обратной совместимости между командами. Изменения проходят через merge-реквесты, которые не допускаются в прод даже на командный стенд, если не прошли нужные интеграционные проверки. Это работает как safety net: если что-то ломается — мы это сразу видим и чиним. Тесты упали? Мы не выкатим фичу.

Итоги

Этот проект —  классический пример сложного финтех-продукта с кучей интеграций. Без моков он бы просто не тестировался. Теперь мы можем:

  • эмулировать нестабильные и нестандартные ответы партнеров;

  • не зависеть от их политики или технических сбоев;

  • не сжигать бюджет на каждый прогон автотестов;

  • ускорить регрессы, наладить нагрузку и доставлять фичи быстрее.

Моки — это не только про тестирование. Это про зрелость процесса. Про уверенность, что твой релиз не развалится, потому что кто-то внезапно сменил регистр в названии машины.

Если интересно — можем отдельно рассказать, как устроен наш кастомный мок-сервис и как его можно развивать дальше. Пишите вопросы в комментариях, с удовольствием обсудим!

Что еще почитать

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