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

Как же проводить интеграционное тестирование в таких условиях?

В моем опыте был случай, когда интеграция представляла собой связку около 15 систем, каждая из которых имела свою базу данных. Все сервисы разворачивались в k8s вручную, тестовые данные были неконсистентны, интеграции между сервисами приходилось настраивать вручную самостоятельно. Ни один сервис нельзя было замокать: каждый элемент влиял на тестируемую бизнес-логику. Я просто познавала дзен, разбираясь во внутреннем устройстве систем и следуя заранее составленному тест-плану. 

Меня зовут Катя Назмеева, сейчас я тестирую бэк в Lamoda Tech. В статье я предложу стратегии для успешного проведения интеграционного тестирования микросервисов и расскажу про инструменты, которые могут облегчить этот процесс. Мы обсудим, как организовать все таким образом, чтобы интеграционное тестирование не создавало задержек в новых релизах — и не заставляло QA страдать.

Дисклеймер

В этом тексте я часто буду использовать слово «интеграция». Предлагаю под интеграцией понимать две или более связанных частей системы (например, сервисы, базы данных и так далее).

Осторожно, legacy

Если в ваши обязанности входит подготовка тестовых данных (заполнение баз данных, написание фикстур), развертывание всех необходимых сервисов и указание правильных переменных окружения, то при интеграционном тестировании (ИТ) микросервисов легко натолкнуться на подводные камни.

Как понять, что перед вами устаревшая система? И какие проблемы с ней могут быть?

  1. Отсутствие документации. Будьте готовы, что придется много общаться с коллегами, проводить reverse engineering, писать тест-кейсы на основе логов.

  2. Ограниченная поддержка и обновления. Вероятно, используемые технологии уже устарели. Значит, вам придется устанавливать дополнительный мониторинг и продумывать план замены.

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

  4. Трудности в диагностике и отладке. Возможно, нужно будет устанавливать дополнительный мониторинг, добавлять необходимые и удалять ненужные логи.

  5. Отсутствие автотестов. В старых системах часто можно столкнуться с тем, что автотестов нет или они устарели. Будет резонно писать тесты по мере изучения системы, руководствуясь правилом «один кейс — одна проверка».

Что же делать дальше? 

В первую очередь — вдохнуть. Выдохнуть. Вдохнуть еще раз. И подготовиться ко встрече со всеми сложностями интеграций микросервисов и устаревших систем.

Когда я только устроилась в Lamoda Tech, я не знала практически ничего о проведении интеграционных тестов старых систем. И тем более о том, как разворачивать их в Kubernetes и настраивать между ними взаимодействие. В тот раз я столкнулась с дичайшими синдромом самозванца, так как у меня в голове не было структурного понимания, как тестировать в подобных условиях. 

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

Составляйте тест-план как можно раньше

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

Во время планирования интеграционных тестов я придерживаюсь структуры:

1. Список сервисов

Сначала нужно подготовить список сервисов, в которых будут произведены изменения, а также сервисов, на работу которых эти изменения повлияют. Это поможет получить целостную картину. Идеально будет представить в виде UML-диаграммы. Для построения UML-диаграмм я использую lucid.app. Пример такой диаграммы приведу в следующем пункте.

Отдельно стоит выделить сервисы, с которыми будет происходить прямое взаимодействие, и те, с которыми будет происходить косвенное взаимодействие. Сервисы, с которыми будет происходить косвенное взаимодействие, можно замокать.

2. Использование заглушек

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

Допустим, вашей командой была реализован новый API в сервисе A. Изменения в сервисе A влияют на работу сервисов B и C, у них прямая интеграция. Сервис C обрабатывает полученные данные и передает их сервису D, т. е. доработки в A могут косвенно повлиять на работу D. 

У A и D — косвенная интеграция. А что, если D интегрируется еще с 20 сервисами? Не поддерживать же их работу в QA окружении — это трудозатратно и с точки зрения человеко-часов, и с точки зрения ресурсов инфраструктуры. 

3. Ссылки на репозитории

Стоит приложить ссылки на репозитории каждого сервиса для дебага и анализа спецификаций.

4. Права доступа на билд и деплой

Важно убедиться в том, что у QA есть достаточные права для билда и деплоя на QA окружение. Если прав недостаточно, нужно заранее уточнить, какие именно права необходимо запросить. 

5. Доступ к тестовым базам данных

Нужно проверить доступ и указать credentials для подключения к тестовым базам данных.

6. Тестовые данные

Если тестовые данные уже существуют в формате фикстур, дампов или скриптов, необходимо прикрепить ссылки на них.

7. Проверка работоспособности сервисов в QA-окружении

Если вы разворачиваете сервисы для тестирования самостоятельно, а не пользуетесь подготовленными девопсами или разработчиками стендами, приступить к настройке сервисов в QA-окружении необходимо настолько рано, насколько это возможно. И заранее убедиться в его работоспособности.

Не раз сталкивалась с ситуацией, когда казалось, что сервис достаточно задеплоить, у него поднимется база данных, затем связать его env-переменными со своим сервисом — и начать тестировать. Но не тут-то было! 

Невыдуманная история из жизни

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

Окей, фикстуры вместе с миграциями не катятся, идем в логи прода и набираем необходимые данные для теста (да, QA в этой команде уже давно нет). А, так их недостаточно, и там еще и персональные данные хранятся… 

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

Поднимать его я уже не стала, поставила задачу на девопса для того, чтобы вместе с деплоем сервиса поднимался еще и WireMock, и замокала тот, в который было обнаружено обращение за нужными мне данными. И интеграция заработала.

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

8. Тест-кейсы

Если какие-либо тест-кейсы, необходимые для проведения ИТ, уже имеются, стоит приложить ссылки на них и описать, какой функционал необходимо протестировать (например, с помощью тест-ранов). 

9. Эпики и задачи

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

10. Определение критериев завершения тестирования

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

11. Риски и план смягчения

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

Налаживайте процессы заранее

Подложить соломку лучше еще до того, как приступили к тестированию. Если вы знаете, что в компании есть устаревшие системы, то лучше действовать на опережение, чем постоянно натыкаться на сложности.

  • Автоматизируйте все, что можно — от разворачивания окружения до запуска тестов. Когда тесты запускаются при каждом изменении и отправке кода в удаленный репозиторий, а результаты доступны всей команде разработки, это удобно и просто красиво.

  • Держите автотесты и тест-кейсы в актуальном состоянии. Часто на документацию просто не хватает времени, но она необходима для прозрачности процесса тестирования и дальнейшего расследования багов.

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

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

  • Настройте алерты для всех необходимых метрик. При этом важно соблюдать баланс – в них не должно быть и лишних логов.

  • Применяйте практики контрактного тестирования (Contract Testing). Представляет собой тестирование каждой точки интеграции по отдельности, заменяя остальные сервисы и базы данных заглушками. Во время тестирования необходимо убедиться в том, что контракты между сервисами правильно определены и обновлены при изменениях.

Тестируйте технические требования

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

Ниже я приведу примеры проблем, которые можно выявить еще на этапе тестирования технических требований, если вовремя его провести.

1. Несовместимость форматов данных

А также различия в структурах и типах  данных, версий API, кодировках и локализациях. 

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

2. Различия в протоколах взаимодействия

Они возникают, если сервисы используют различные коммуникационные протоколы (например, HTTP, gRPC, AMQP) и различные версии одного протокола.

При использовании подобных технологий QA стоит учитывать в тестах проверку на протокол и обращать внимание на то, указывается ли протокол в логах.

3. Проблемы с синхронизацией

Возникают тогда, когда сервисы не могут согласованно обмениваться данными или выполнять операции в ожидаемой последовательности. Это приводит к проблеме несогласованной обработки событий и ведет к состояниям гонок (race conditions). То есть очередность обработки асинхронных событий может нарушиться из-за различной нагрузки или скорости выполнения, где результат зависит от выполнения операций.

Чтобы этого избежать, учитывайте в тестах проверку согласованности обработки событий и последовательности выполнения запросов по логам (Kibana, Elasticsearch, Grafana, Prometheus).

Приведу пример из практики. В проекте был реализован API, который слушал топик в Kafka и передавал статус в стейт-машину, которая обрабатывала только корректные статусы. Из-за ошибок в процессе обработки сообщений API иногда отправлял неверные статусы в стейт-машину, что приводило к валидационным ошибкам. 

Я обнаружила ошибку, создала тесты, имитирующие ошибочные статусы, предложила добавить промежуточную валидацию статусов в API перед отправкой. В результате API стал фильтровать некорректные статусы. Это обеспечило правильную работу стейт-машины и синхронизацию сервисов.  

Используйте удобные инструменты

Я использую в своей работе пару неочевидных инструментов, которые хочу посоветовать и вам.

Могу смело сказать, что WireMock — библиотека для работы со стабами и моками  — это антидепрессант в мире интеграционных тестов. Она достаточно проста использовании, так как поддерживает конфигурацию через JSON. 

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

  • запрос был отправлен на определенный URL, 

  • проверка количества запросов, 

  • проверка параметров в заголовках 

  • проверка тела запроса.

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

Для интеграционных тестов в Lamoda мы также используем наш инструмент для автотестов Gonkey. Gonkey обладает очень низким порогом входа. Сам он написан на Go, но тестировщику достаточно уметь писать SQL-запросы и разбираться в YAML-структуре, ведь сами тест-кейсы QA пишет в YAML-формате. Кейсы хранятся в репозитории проекта, что очень удобно, ведь кейсы прилагаются сразу к проекту.

Итоги

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

Надеюсь, мои советы и опыт помогут вам справиться с интеграционными тестами легче и быстрее.

А для тех, кто дочитал — бонус: чек-лист для проведения интеграционного тестирования. Делитесь им с коллегами и используйте в работе: надеюсь, он вам пригодится!