Как тестировать микросервисы

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

Итак, как же тестировать микросервисные приложения? Применима ли в этом случае классическая пирамида тестирования? Как тестировать, если вовлечены сторонние сервисы и возможны сбои в сети?

Проблемы

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

  • Распределенность: микросервисы развертываются на нескольких серверах, возможно расположенных в разных географических точках, что увеличивает задержки и подвергает приложение риску сетевых сбоев. Тесты могут падать не по причине ошибок в коде, а по причине сбоев в сети, что тормозит конвейер CI/CD и блокирует разработку.
  • Автономность: пока они не нарушают совместимость с API, команды разработчиков могут развертывать свои микросервисы в любое время.
  • Увеличенная область тестирования: поскольку каждый микросервис имеет как минимум несколько API-эндпойнтов, нужно охватить гораздо больше тестируемых поверхностей.
  • Много языков программирования: команды разработчиков могут выбирать лучший, по их мнению, язык для своего микросервиса. В большой системе вряд ли удастся найти единый фреймворк, который будет пригоден для тестирования всех компонентов.
  • Production: поскольку микросервисы развертываются независимо друг от друга и создаются автономными dev-командами, требуются дополнительные проверки, чтобы гарантировать, что все они будут корректно функционировать в связке после развертывания.

Все эти особенности микросервисов заставляют выбирать новые стратегии тестирования.

Тестовая пирамида микросервисов

Пирамида тестирования — инструмент планирования автоматизированного тестирования. В своей традиционной форме пирамида содержит три типа тестов:

  • модульные
  • интеграционные
  • сквозные

В тестовой пирамиде микросервисов добавляются два новых типа: компонентные и контрактные тесты.

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

Юнит-тесты

Модульные тесты — одна из самых трудозатратных форм тестирования, из-за большого количества самих тестов. Юнит состоит из класса, метода или функции, которые должны быть протестированы изолированно. Юнит-тестирование является очень важной частью таких практик разработки, как Test-Driven Development или Behavior-Driven Development.

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

  • Одиночные (Solitary) юнит-тесты: их следует использовать, когда нам нужно, чтобы результат тестирования всегда был детерминированным, то есть определенным. Мы используем моки и стабы, чтобы изолировать тестируемый код от внешних зависимостей.
  • Общительные (Sociable) юнит-тесты: общительным тестам разрешено вызывать другие сервисы. В этом режиме мы переносим сложность теста в тестовое или staging-окружение. «Общительные» тесты не являются жестко детерминированными, но мы можем быть более уверены в их результатах, если они прошли.

Мы можем выполнять юнит-тесты изолированно, используя тестовые дублеры. В качестве альтернативы мы можем позволить протестированному коду вызывать другие микросервисы, и в этом случае мы говорим об «общительных» тестах.

Как увидите далее, баланс между уверенностью и стабильностью будет постоянной темой статьи. Моки (Solitary) ускоряют работу и уменьшают неопределенность, но чем больше вы их создаете, тем меньше можете доверять результатам. Sociable-тесты, несмотря на свои недостатки, более реалистичны. Поэтому, скорее всего, вам придется искать баланс между ними.

Если хотите ознакомиться с примерами таких тестов, посмотрите этот хороший пост.

Контракты

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

Тесты контрактов гарантируют, что микросервисы «соблюдают контракт». Они не проверяют поведение сервиса, а лишь гарантируют, что входы и выходы имеют ожидаемые характеристики и что сервис работает в пределах допустимого времени и производительности.

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

  • Контрактные тесты на стороне потребителя пишутся и выполняются командой downstream team. Во время тестирования микросервис подключается к фейк-версии или мок-версии сервиса-провайдера, чтобы проверить, может ли он использовать его API.
  • Контрактные тесты на стороне провайдера выполняются на upstream-сервисе. Этот тип тестов эмулирует API-запросы, которые могут выполнять клиенты, проверяя, соответствует ли провайдер контракту. Тесты на стороне провайдера позволяют разработчикам проверять совместимость.

Контрактные тесты могут выполняться как в восходящем, так и нисходящем направлении. Тесты на стороне провайдера проверяют, что сервис не имплементирует изменений, которые могут нарушить работу связанных сервисов. Тесты на стороне потребителей запускают компонент на стороне потребителя против имитированной версии производителя (не реального провайдера), чтобы убедиться, что потребитель может делать запросы и получать ожидаемые ответы от провайдера. Для воспроизведения HTTP-запросов мы можем использовать такие инструменты, как Wiremock.

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

Интеграционные

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

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

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

Здесь хороший пост о стабах микросервисов.

Компонентные

Компонент — это микросервис или набор микросервисов, которые выполняют определенную роль в общей системе.

Тестирование компонентов — это тип приемочного тестирования, при котором мы изучаем поведение компонента в изоляции, заменяя сервисы симулированными ресурсами или имитируя их.

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

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

Существует два способа тестирования компонентов: In-process и Out-of-process.

In-process компонентное тестирование

В этом подклассе компонентного тестирования тест-раннер существует в том же потоке или процессе, что и микросервис. Мы запускаем микросервис в «автономном режиме тестирования», где все его зависимости имитируются, что позволяет нам запускать тест без выхода в сеть.

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

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

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

Мы можем писать компонентные тесты с помощью любого языка или фреймворка, но самыми популярными, пожалуй, являются Cucumber и Capybara.

Out-of-process компонентное тестирование

Out-of-process-тесты подходят для компонентов любого размера, включая те, которые состоят из множества микросервисов. При этом типе тестирования компонент развертывается — без изменений — в тестовом окружении, где все внешние зависимости «закрыты» моками или стабами.

В этом типе компонентных тестов сложность выносится в тестовое окружение, которое должно реплицировать остальную часть системы.

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

Сквозные

До сих пор мы рассматривали систему по частям. Юнит-тесты использовались для тестирования юнитов микросервиса, контрактные тесты проверяли совместимость с API, интеграционные тесты проверяли сетевые вызовы, а компонентные тесты использовались для проверки поведения подсистем. Только на самом верху пирамиды автоматизированного тестирования мы тестируем всю систему.

Сквозное тестирование (E2E) гарантирует, что система отвечает потребностям пользователей и достигает их бизнес-целей. Тест-сьют E2E должен охватывать все микросервисы приложения, используя те же интерфейсы, что и пользователи — часто с сочетанием тестов пользовательского интерфейса и API.

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

End-to-end тесты микросервисов —  автоматизированные тесты, имитирующие взаимодействие с пользователем. Могут имитироваться только внешние сторонние сервисы.

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

Итак

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

Некоторые ПМ могут решить, что им вполне достаточно юнит, контрактных и компонентных тестов, выполняемых разработчиками. Другим может понадобиться полное интеграционное и сквозное тестирование, и они могут привлечь QA-команду для более надежного охвата.»

Источник


Также: Нагрузочное тестирование микросервисов

Какой была ваша первая зарплата в QA и как вы искали первую работу?

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

Подписаться
Уведомить о
guest

0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии

Мы в Telegram

Наш официальный канал
Полезные материалы и тесты
Готовимся к собеседованию
Project- и Product-менеджмент

? Популярное

? Telegram-обсуждения

Наши подписчики обсуждают, как искали первую работу в QA. Некоторые ищут ее прямо сейчас.
Наши подписчики рассказывают о том, как не бояться задавать тупые вопросы и чувствовать себя уверенно в новой команде.
Обсуждаем, куда лучше податься - в менеджмент или по технической ветке?
Говорим о конфликтных ситуациях в команде и о том, как их избежать
$1100*
медианная зарплата в QA в июне 2023

*по результатам опроса QA-инженеров в нашем телеграм-канале

Собеседование

19%*
IT-специалистов переехало или приняло решение о переезде из России по состоянию на конец марта 2022

*по результатам опроса в нашем телеграм-канале

live

Обсуждают сейчас