Фото Michael Schiffer on Unsplash
Микросервисное приложение — это группа распределенных программ, которые обмениваются данными по сети, иногда взаимодействуя со сторонними сервисами и базами данных. Микросервисы, в силу своей сетевой природы, обеспечивают больше точек отказа, чем традиционный монолит. В результате этого нам нужен другой, более широкий подход к тестированию.
Итак, как же тестировать микросервисное приложение? Работает ли еще пирамида тестирования? Как тестировать, когда задействованы сторонние сервисы и возможны сбои в работе сети? Мы попытаемся ответить на все эти вопросы в этой статье.
- Сложности тестирования микросервисов
- Пирамида тестирования для микросервисов
- Модульные тесты для микросервисов
- Тестирование по контракту
- Интеграционные тесты для микросервисов
- Компонентные тесты для микросервисов
- Внутрипроцессное тестирование компонентов
- Внепроцессное тестирование компонентов
- Сквозное тестирование в микросервисах
- Заключение
Сложности тестирования микросервисов
Микросервисная архитектура — это настолько глубокий сдвиг парадигмы, что мы должны пересмотреть традиционные методы тестирования. Микросервисы отличаются от классической монолитной структуры по многим параметрам:
- Распределенность: микросервисы развертываются на нескольких серверах, потенциально в разных географических точках, что увеличивает задержки и подвергает приложение риску сетевых сбоев. Тесты, которые полагаются на сеть, могут выйти из строя не по вине кода, прерывая конвейеры CI/CD и блокируя разработку.
- Автономность: пока они не нарушают совместимость с API, команды разработчиков могут свободно развертывать свои микросервисы в любое время.
- Увеличенная область тестирования: поскольку каждый микросервис открывает по крайней мере несколько конечных точек API, существует гораздо больше тестируемых поверхностей.
- Полиглот: команды разработчиков могут выбрать лучший язык для своего микросервиса. В большой системе вряд ли удастся найти единый тестовый фреймворк, который будет работать для всех компонентов.
- Производство — движущаяся цель: поскольку микросервисы развертываются независимо друг от друга и создаются автономными командами, требуются дополнительные проверки и границы, чтобы убедиться, что все они будут корректно функционировать вместе после развертывания.
Все эти характеристики заставляют нас думать о новых стратегиях тестирования.
Пирамида тестирования для микросервисов
Пирамида тестирования — это инструмент планирования для автоматизированного тестирования программного обеспечения. В своей традиционной форме пирамида использует три типа тестов:
- модульные тесты
- интеграционные тесты
- сквозные тесты.
Пирамида микросервисов добавляет два новых типа: компонентные и контрактные тесты.
Рассмотрим подробнее, как работает каждый слой пирамиды.
Модульные тесты для микросервисов
Юнит-тесты — это одна из самых тонких и многочисленных форм тестирования. Блок состоит из класса, метода или функции, которые можно тестировать изолированно. Юнит-тестирование является неотъемлемой частью таких практик разработки, как Test-Driven Development или Behavior-Driven Development.
По сравнению с монолитом, у модуля в микросервисе гораздо больше шансов потребовать сетевого вызова для выполнения своей функции. Когда это происходит, мы можем либо позволить коду обратиться к внешнему сервису — принимая некоторую задержку и неопределенность — либо заменить вызов тестовым дублем, что дает нам два способа работы с зависимостями микросервиса:
- Одиночные модульные тесты: их следует использовать, когда нам нужно, чтобы результат тестирования всегда был детерминированным. Мы используем мокинг или заглушки, чтобы изолировать тестируемый код от внешних зависимостей.
- Общительные модульные тесты: общительным тестам разрешено вызывать другие сервисы. В этом режиме мы переносим сложность теста в тестовую среду или среду постановки. Общительные тесты не являются детерминированными, но мы можем быть более уверены в их результатах, если они пройдут.
Как вы увидите, баланс между уверенностью и стабильностью будет постоянной темой всего этого поста. Насмешки ускоряют работу и уменьшают неопределенность, но чем больше вы насмехаетесь, тем меньше вы можете доверять результатам. Общительные тесты, несмотря на их недостатки, более реалистичны. Поэтому, скорее всего, вам придется найти хороший баланс между обоими типами.
Если вы хотите ознакомиться с примерами одиночных и общительных тестов, посмотрите этот хороший пост Дилана Уотсона на dev.to.
Тестирование по контракту
Контракт формируется всякий раз, когда два сервиса соединяются через интерфейс. Контракт определяет все возможные входы и выходы с их структурами данных и побочными эффектами. Потребитель и производитель сервиса должны следовать правилам, указанным в контракте, чтобы взаимодействие было возможным.
Тесты контракта обеспечивают соблюдение микросервисами их контракта. Они не проверяют поведение сервиса; они только гарантируют, что входы и выходы имеют ожидаемые характеристики и что сервис работает в пределах допустимого времени и производительности.
В зависимости от отношений между сервисами, тесты контракта могут выполняться производителем, потребителем или обоими.
- Контрактные тесты со стороны потребителя пишутся и выполняются командой нисходящего потока. Во время тестирования микросервис подключается к поддельной или имитированной версии сервиса-производителя, чтобы проверить, может ли он использовать его API.
- Тесты контрактов на стороне производителя выполняются в службе upstream. Этот тип тестов эмулирует различные API-запросы, которые могут делать клиенты, проверяя соответствие производителя контракту. Тесты на стороне производителя дают разработчикам знать, когда они собираются нарушить совместимость для своих потребителей.
Если обе стороны контрактных тестов прошли, то производители и потребители совместимы и могут взаимодействовать. Тесты контрактов всегда должны выполняться в процессе непрерывной интеграции для выявления несовместимости до развертывания.
Вы можете поиграть с тестированием контрактов в режиме онлайн в 5-минутном руководстве по началу работы с Pact. Pact — это инструмент тестирования на основе HTTP для написания и запуска контрактных тестов на основе потребителей и производителей.
Интеграционные тесты для микросервисов
Интеграционные тесты для микросервисов работают несколько иначе, чем в других архитектурах. Их цель — выявить дефекты интерфейса, заставив микросервисы взаимодействовать. В отличие от контрактных тестов, где одна сторона всегда имитируется, интеграционные тесты используют реальные сервисы.
Интеграционные тесты не заинтересованы в оценке поведения или бизнес-логики сервиса. Вместо этого мы хотим убедиться, что микросервисы могут взаимодействовать друг с другом и своими собственными базами данных. Мы ищем такие вещи, как отсутствующие HTTP-заголовки и несоответствующие пары запрос/ответ. И, как результат, интеграционные тесты обычно реализуются на уровне интерфейса.
Посмотрите пост Виталия Баума о заглушках микросервисов, чтобы увидеть интеграционное тестирование кода в действии.
Компонентные тесты для микросервисов
Компонент — это микросервис или набор микросервисов, который выполняет определенную роль в рамках более крупной системы.
Тестирование компонентов — это тип приемочного тестирования, при котором мы исследуем поведение компонента в изоляции, заменяя сервисы симулированными ресурсами или имитацией.
Тестирование компонентов является более тщательным, чем интеграционное тестирование, потому что оно проходит по счастливым и несчастливым путям — например, как компонент реагирует на имитацию сбоев в сети или неправильно сформированные запросы. Мы хотим знать, удовлетворяет ли компонент потребностям своего потребителя, точно так же, как мы это делаем при приемочном или сквозном тестировании.
Существует два способа проведения тестирования компонентов: в процессе и вне процесса.
Внутрипроцессное тестирование компонентов
В этом подклассе компонентного тестирования программа тестирования существует в том же потоке или процессе, что и микросервис. Мы запускаем микросервис в «автономном режиме тестирования», где все его зависимости имитируются, что позволяет нам запустить тест без сети.
Внутрипроцессное тестирование работает только в том случае, если компонент представляет собой один микросервис. На первый взгляд, компонентные тесты очень похожи на сквозные или приемочные тесты. Разница лишь в том, что компонентные тесты выбирают одну часть системы (компонент) и изолируют ее от остальных. Компонент тщательно тестируется, чтобы убедиться, что он выполняет функции, необходимые его пользователям или потребителям.
Мы можем писать компонентные тесты с помощью любого языка или фреймворка, но самыми популярными, вероятно, являются Cucumber и Capybara.
Внепроцессное тестирование компонентов
Внепроцессные тесты подходят для компонентов любого размера, включая те, которые состоят из множества микросервисов. При этом типе тестирования компонент развертывается — без изменений — в тестовой среде, где все внешние зависимости имитируются или отбрасываются.
Чтобы завершить концепцию контрактного тестирования, вы можете изучить примеры кода для контрактного тестирования на Java Spring. Также, если вы Java-разработчик, в этом посте есть примеры кода для тестирования Java-микросервисов на всех уровнях.
Сквозное тестирование в микросервисах
До сих пор мы тестировали систему по частям. Юнит-тесты использовались для тестирования частей микросервиса, контрактные тесты проверяли совместимость с API, интеграционные тесты проверяли сетевые вызовы, а компонентные тесты использовались для проверки поведения подсистемы. Только на самом верху пирамиды автоматизированного тестирования мы тестируем всю систему.
Сквозное тестирование (E2E) гарантирует, что система отвечает потребностям пользователей и достигает их бизнес-целей. Набор E2E должен охватывать все микросервисы в приложении, используя те же интерфейсы, что и пользователи — часто с комбинацией тестов пользовательского интерфейса и API.
Приложение должно работать в среде, максимально приближенной к производственной. В идеале тестовая среда должна включать все сторонние сервисы, которые обычно нужны приложению, но иногда их можно имитировать, чтобы сократить расходы или предотвратить злоупотребления.
Как показано на пирамиде тестирования, тесты E2E наименее многочисленны, что хорошо, поскольку их обычно труднее всего запускать и поддерживать. Если мы сфокусируемся на путешествиях пользователей и их потребностях, мы сможем извлечь много пользы всего из нескольких тестов E2E.
Заключение
Иная парадигма требует изменения стратегий. Тестирование в микросервисной архитектуре важно как никогда, но нам необходимо скорректировать наши методы, чтобы они соответствовали новой модели разработки. Система больше не управляется одной командой. Вместо этого каждый владелец микросервиса должен внести свой вклад в обеспечение работы приложения в целом.
Некоторые организации могут решить, что достаточно юнит-, контрактных и компонентных тестов. Другие, не удовлетворенные отсутствием сквозного и интеграционного тестирования, могут решить создать команду QA для обеспечения межкомандного тестирования.
Хотите узнать больше о микросервисах? Ознакомьтесь с этими статьями:
- Что такое микросервисная архитектура?
- Принципы проектирования микросервисов с учетом доменов
- Управление релизами для микросервисов
- 12 способов улучшить ваш монолит перед переходом на микросервисы
Спасибо за чтение!