Тестирование микросервисов

Тестирование микросервисов. Справочная информация. Пополняемый и перерабатываемый материал.

О важности тестирования

Допустим, вы решили перейти на микросервисы (или уже почти там там). Только с помощью тестов можно проверить и подтвердить, что постепенно меняющаяся архитектура остается пригодной. Но почему еще тестирование микросервисов важно начинать с самых ранних этапов перехода?

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

Второй момент, — все мы знаем подход shift left (например — выполнять часть тестов производительности и безопасности еще на этапе разработки), когда практики качества серьезно сдвигаются влево. В микросервисах, помимо shift left появляется shift right — тестирование на продакшене (тот же chaos engineering, нагрузка и scalability на проде). Только на проде можно получить самые точные результаты тестов, так это именно то окружение, со своими достоинствами и недостатками, которым пользуется клиент.

Некоторые виды тестирования

Юнит-тесты

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

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

Юнит-тесты можно условно поделить на те, что тестируют состояние объекта и те, что тестируют поведение/взаимодействие, используя тест-дублеры.

Первые находятся в центре дизайна, слое доменной логики. Его особенность в том, что если удалось провести качественное проектирование на уровне предметной области, то изменения в него будут вносится все реже и реже(и, скорее всего, точечно), — это стабильная область (в отличие, например, от UI). Следствие из этого — код и нюансы его работы будут забываться, тесты станут стабильнее и выступать в том числе актуальной документацией, а их запуск в рамках CI является формальным подтверждением того, что в сборку не попал регрес.

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

Если планируется переход от монолита к микросервисам, то юнит-тесты с использованием дублеров могут разделиться на две части. Чтобы лучше это понять, следует обратиться к шаблону «Микросервисное шасси/Фреймворк микросервиса» — от вынесение общей инфраструктурной функциональности в отдельный фреймворк. Тесты общего назначения могут уйти в кодовую базу фреймворка и поддерживаться отдельно, что снижает общую сложность управления тестами и сокращает время их выполнения (в CI сервиса выполняются юнит-тесты сервиса, а юнит тесты фреймворка выполняются в своем CI).

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

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

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

При проверке интеграций с другими сервисами эти тесты должны выявлять ошибки уровня протокола: пропущенные HTTP-заголовки, корректную работу с SSL, обработку ошибок. Медленные ответы и ошибки можно проверить в помощью стабов (stubs).

Интеграционные тесты на хранилище должны давать уверенность в том, что схема данных в хранилище соответствует реализации в коде (да-да, DDL в общем случае создает API для доступа к данным, а DML с определенной долей свободы толкования можно назвать протоколом взаимодействия). Тесты этого типа включают в себя сохранение/возврат сохраненных данных (транзакции), обработку ошибок сети и таймауты.

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

average dose of levitra argumentative essay on boxing sildenafil savings card buy lisinopril online canada abbreviation https://businesswomanguide.org/capstone/dissertation-comment-faire-introduction/22/ here asid blockers affect viagra buy viagra in uae go how to cite an essay in print with pages https://oaksofwellington.com/promethazine-25-mg-recreational-use/ long term goals essay examples https://vivianschilling.com/film/essays-on-the-scientific-method/61/ get link change myself essay nuova pubblicit fiat viagra catcher in the rye essay about depression cialis take a month to work for bph an essay concerning toleration https://greenacresstorage.net/thesis-game-based-learning/ que efectos causa el viagra en las mujeres viagra spray in ksa follow link 40 40 viagra what does kamagra tablets do here essays on gertrude from hamlet optimization research papers follow site newspaper report essay format Компонент — любая хорошо инкапсулированная, целостная и независимо заменяемая часть большей системы.

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

В микросервисной архитектуре сам микросервис и есть компонент.

Компоненты можно тестировать с сетевым взаимодействием и с изоляцией сетевого взаимодействия.

Проверка с изоляцией сетевого взаимодействия достигается за счет Dependency Injection и in-memory решений.

Примеры библиотек для изоляции HTTP-сервера

  • .NET: TestServer
  • Java: Jetty

Примеры библиотек для изоляции хранилища

  • H2 Database Engine в Java
  • LiteDB в .NET

Проверка с сетевым взаимодействием помимо поведения позволяет проверить корректность сетевых настроек и возможность обрабатывать сетевые запросы. Реальный сервис заменяется на Stub Service.

Возможные реализации Stub Service

  • Использование инструментов Mock’ирования
  • Собственная разработка
  • Record-replay

Примеры инструментов:

Контрактные тесты

Контракт – это соглашение между потребителем и поставщиком сервиса, включающее в себя:

  • Входные структуры данных
  • Выходные структуры данных
  • Обработку ошибок
  • Производительность
  • Параллелизм

Контрактные тесты разрабатывает потребитель (ServiceA-B-C), после чего они помещаются в тестовый набор Producer’а.

При обратно не совместимых изменениях Producer будет знать, какого потребителя уведомить. Пример инструмента для контрактного тестирования — pact.io.

End-to-end тесты

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

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

При e2e-тестировании с нестабильными внешними системами, над которыми нет контроля, стоит задуматься о применении Stub’ов

Стратегия тестирования микросервисов

Простая стратегия в виде пирамиды тестирования все еще применима, но в нам потребуется посмотреть на нее немного глубже.

Тестирование в микросервисах наследует идеи, заложенные в DevOps и подразумевает участие всей команды в обеспечении качества:

  • Тестирование идей
  • Направление разработки через бизнес-ориентированные тесты
  • Тестирование и разработка взаимно интегрированы, рассматриваются как единое целое
  • Команда устраняет ошибки немедленно
  • Вся команда вовлечена в мониторинг и тестирование на проде

Классический вопрос:

  • На каком уровне автоматизировать тесты?
    • Какую часть приложения должен проверять каждый тест?
      • Идея в том, чтобы минимизировать количество слоев приложения, через которые проходит каждый тест

Инфраструктура

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

Стоит начать с выявления проблемных мест, например:

  • Тестовая среда в нерабочем состоянии
  • Установлена неверная версия продукта
  • Артефакты поставки невозможно задеплоить
  • В тестовой базе поврежденные данные
  • Несколько тестов используют одни и те же данные
  • Данные не prod-like

Новые возможности, предоставляемые инфраструктурой при применении микросервисного архитектурного стиля:

  • Инструментальное создание сред с помощью скриптов
  • Требования железа сместились с локальных сред к контейнерам и виртуалкам
  • Локальные машины могут быть prod-like

Нельзя обойти стороной и преимущества тестирования на облачной инфраструктуре:

  • Дешевле
  • Проще реализовать
  • Возможность параллельного выполнения
    • Одновременное тестирование в нескольких браузерах
    • Масштабируемость и повторяемость

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

Инфраструктура как код

В работе с инфраструктурой при использовании микросервисов без подхода Инфраструктра как код (IaC, Infrastructure as a Code) никуда. Инфраструктура == код. Код нужно тестировать. Что проверять?

  • Код, конфигурирующий и настраивающий deployment pipeline
  • Скрипты деплоя
  • Подключения, надежность, failover’ы, бэкапы и восстановления
  • Мониторинг прода
Слайд с моего выступления по BDD в IaC

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

Обучение на продакшене

Задумаемся на минутку. Бывали ли в вашей жизни сюрпризы после выхода в прод?

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

  • Test/Staging ≠ Production
  • Поведение пользователя не предсказуемо
  • Недостаток времени на тестирование всех возможных в природе сценариев

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

Способы получения информации о здоровье сервиса:

  • «Blackbox» мониторинг — опрос сервисов, не дает информации о причинах отказов
  • «WhiteBox» мониторинг — отчеты о внуреннем состоянии (дебаггин, dashbord’ы, предиктивный анализ)
  • Логи — дискретные событие, происходящие во времени (например в JSON)

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

Логирование должно быть централизированным и структурированным (json), но может оказаться дорогим.

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

Метрики же — это числа во времени. Они оптимизируются для хранения. Их дешевле и проще хранить. Например Prometheus или ELK.

Наблюдаемость (Obesrvability) — это возможность задать произвольный вопрос об окружении, —  и это ключевая часть, — не зная заранее что вы хотите спросить.

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

Другой способ тестирования на проде — Chaos Engineering. Как и наблюдаемость, он позволяет выявлять неизвестные неизвестные. Заключается в проведении контрлируемых экспериментов на проде и требует мониторинга и наблюдаемости.

Примеры инструментов:

Заключение

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

Share

Добавить комментарий