Перевод статьи https://labs.spotify.com/2018/01/11/testing-of-microservices/
С какой целью мы создаем тесты?
Многие утверждают, что мы создаем тесты для того, чтобы удостовериться, что все работает как положено. Однако это частично соответствует реальности. В конце концов, тесты вручную также могут подтверждать правильную работу.
Каждый, кто когда-либо совершал тестирование вручную, знает, что это процесс медленный, скучный и от ошибок не застрахованный . Создавая автоматические тесты, мы стараемся убрать или, по крайней мере, улучшить эти проблемные места.
Мы хотим продвигаться быстро, с уверенностью, что все работает. Поэтому тест должен:
- Давать нам уверенность, что код работает как следует;
- Предоставлять быстрый, точный, надежный и предсказуемый отклик;
- Упростить обслуживание, что обычно не берется во внимание при написании тестов.
В мире Микросервисов достижение 3 этих пунктов считается искусством. Давайте посмотрим на традиционную стратегию тестирования и ее недочеты, чтобы мы могли продвинуться в вопросе улучшения тестирования Микросервисов.
Традиционная стратегия тестирования
Многие знакомы с известной Пирамидой Тестирования.
Традиционная Пирамида Тестирования
На протяжении долгого времени, данная пирамида являлась чрезвычайно эффективным способом организации тестов. В мире микросервисов ситуация совершенно другая, более того, мы можем утверждали, что она достаточно вредоносная.
Наибольшая трудность в микросервисах заключается не в самом сервисе, а в том, как он взаимодействует с другими, что заслуживает особого внимания.
Наличие многих модульных тестов в Микросервисах, которые по определению малы, также ограничивает способы того, как мы можем изменить код, не изменяя тесты. Из-за необходимости изменять тесты, мы все меньше уверены в том, что код все еще выполняет свою функцию, и это отрицательно влияет на скорость, с которой мы выполняем итерацию.
Стратегия тестирования Микросервисов
Наиболее подходящим способом структурирования наших тестов для Микросервисов является Сота Тестирования
Сота Тестирования Микросервисов
Это означает, что нам нужно ориентироваться на Интеграционные тесты, иметь несколько тестов детального внедрения и даже меньше Интегрированных тестов (в идеале, один)
Интегрированные тесты
Ссылаясь на отличную презентацию J.B. Rainsberger–«Интегрированные тесты — это мошенничество», мы определяем понятие Интегрированный тест как:
Тест, который сработает или нет, в зависимости от правильности другой системы.
Некоторыми признаками наличия Интегрированных Тестов является:
- Мы запускаем другие службы в локальной тестовой среде
- Мы тестируем другие службы в общей тестовой среде
- Изменения в вашей системе прерывают тесты для других систем
Это довольно хрупкий способ тестирования, поэтому мы рекомендуем детально изучить информацию из первоначального источника, указанного выше.
Интеграционные Тесты
Вместо этого нам нужно стремиться к интеграции тестов, которые проверяют правильность нашего сервиса в более изолированном режиме, сосредотачиваясь на точках взаимодействия и делая их очень явно выраженными.
Давайте, в качестве примера,возьмем сервисы с Spotify. Мы начнем с очень простого сервиса, который зависит только от базы данных SQL и предоставляет клиентам RESTAPI. Все тесты проекта работают по одному и тому же шаблону. Мы запускаем базу данных, заполняем ее, запускаем сервис и запрашиваем фактический API в тестах.В этом сервисе совсем нет тестов детального внедрения, потому, что они нам не нужны.
Тесты детального внедрения нам бы только мешали. Теперь мы можем реорганизовать внутренности, при этом, не касаясь тестов. Мы даже могли бы перенести базу данных из PostgreSQL в NoSQL без изменения методов тестирования. Единственное, что нужно было бы изменить – это установку теста.
Оказывается, что во всем тестовом пакете не очень много тестов, и мы уверены, что они точны, подходят нам и их будет достаточно.
Компромиссом здесь является некоторая потеря скорости во время тестирования. Пакет работает от миллисекунд до нескольких секунд, но мы уверены, что увеличенная скорость кодирования и простота обслуживания все компенсирует.
Речь шла о простом сервисе, работает ли это для более сложных сервисов? Наш ответ – да.
Давайте посмотрим на более сложный сервис. Он использует события из нескольких разных источников, все с их собственными причудами. Затем он сопоставляет данные с необходимой моделью и сохраняет их в базу данных. С другой стороны, сервис предоставляет RESTAPI для приложения React.
Для лучшего понимания, системная диаграмма выглядит следующим образом:
Так как события могут поступать в разном порядке, а любое подмножество событий должно создавать допустимую модель, проверка всех путей кода в режиме «white-box» будет очень сложной.
Вместо этого мы помещаем сообщения в тему pubsub в памяти, используем их и проверяем правильность вывода в API.
Заметка: для краткости мы опустили некоторую установку переменных.
Тесты не усложняются еще больше. Опубликованных сообщений может быть много или мало, но структура остается прежней. Для добавления новых тестов нам нужно сфокусироваться на сообщениях и инструментах API.Точно так же нам нужно сосредоточиваться на точках взаимодействия, поскольку мы подчеркиваем и хорошо практикуем как ввод, так и вывод услуги.
Компромиссом в данном случае будет потеря точности отклика, когда тест терпит неудачу, так как это утверждение говорит только о разнице в фактических и ожидаемых значениях.
Итак, когда использовать тесты детального внедрения (Implementation Detail Tests)?
Мы оставляем данный тип тестов для частей кода, которые естественно изолированы и имеют внутреннюю сложность.
Примером является анализ нашего CI Build лог-файла, для того, чтобы дать значимый отзыв пользователям, так, что им не нужно проходить логи и самостоятельно искать проблемы. Не стоит говорить, что анализ логов – трудное дело. Использование теста детального внедрения помогает нам избежать интеграционного тестирования каждой возможной проблемы сборки. Вместо этого у нас есть один Интеграционный тест, охватывающий неудачную сборку, которая обеспечивает правильное поле сообщения об ошибке.Тестирование детальноговнедрения охватывает различные сценарии сбоев, которые мы ищем в лог файле, и гарантируем, что мы проанализируем их как ожидалось.
Идеи напоследок
Как же все это соответствует нашим целям при написании тестов?
- Дает нам уверенность в том, что код работает как нужно.
Поскольку мы используем реалистичные инструменты в качестве входных данных и ожидаемых результатов, мы знаем, что независимо от того, как код выглядит внутри, он делает то, что должен.
- Предоставляет быстрый, точный, надежный и предсказуемый отзыв.
Как мы уже говорили, это то, где мы вероятно немного отстаем. Отклик, который мы получаем, достаточно быстрый, но когда тесты терпят неудачу, мы просто пытаемся отследить ошибки.
- Упрощает обслуживание, что обычно не берется во внимание при написании тестов.
Поскольку мы тестируем по краям, мы можем выполнять техническое обслуживание и оставить код чище, чем когда его нашли, с уверенностью, что мы его не сломаем. И мы продвигаемся очень быстро.
В качестве бонуса – так как мы используем те же инструменты снова для других сервисов, мы увеличиваем уверенность в том, что мы случайно не нарушим контракты между ними. Они также станут основанием для того, чтобы мы перешли к тестированию, основанному на доверии на основе потребителей (например, PACT), еще более уверенному в том, что контракты не будут, случайно, нарушены.
Кстати, вы могли заметить, что мы расцениваем Микросервис как изолированный компонент, протестированный своими контрактами. В этом смысле Микросервис стал нашей новой отправной точкой или «единицей», поэтому мы избегали использования понятия «Тестирование модулей» для Микросервисов в пользу детальных тестов внедрения.
Микросервисы используют старую идею об изолированных компонентах и показывают нам, что такое абстракции. Пришло время воспользоваться этим и применить наше тестирование в правильных местах.
Удачи!
About The Author
Виктор Карабедянц
ИТ директор (CIO), руководитель нескольких DevOps команд. Профессиональный руководитель проектов по внедрению, поддержке ИТ систем и обслуживанию пользователей.