Mockingbird, или Как убить всех зайцев одним выстрелом |
25.11.2024 00:00 | ||||||||||||||||||||||||||
Привет! Меня зовут Ольга Инеева, я ведущий инженер по обеспечению качества в Т-Банке. Расскажу о проблемах тестирования интеграции и об инструменте для мокирования Mockingbird. Мы решили проблему сложных связанных сценариев и хотим поделиться этим знанием. Добро пожаловать под кат! Проблемы тестирования интеграций Наше подразделение занимается продуктами, связанными с BNPL-политикой (buy now, pay later). Это такие продукты, как автокредит, ипотека, сервис «Долями», потребительские кредиты и другое. У нас много интеграций — как внутрибанковских, так и внешних. На стабильность работы внешних систем мы чаще всего не можем повлиять. Мы столкнулись со множеством проблем при тестировании этих интеграций:
В подобных случаях лучше использовать моки. Моки, или заглушки, –— это эмуляция сервиса, с которым у нас есть интеграция. Главное правило: не увлечься ими, протестировать в рамках e2e-сценариев и живой интеграции, иначе рискуем словить непредвиденный баг. Плюсы мокирования:
Минусы:
Уровень доверия к тестамПредставьте, что в ваших тестах используется реальная интеграция, а не моки. Вы смотрите утром отчет по ночному прогону, и он выглядит так: Вы думаете о том, почему упали 3 теста. Причин может быть много: у стороннего сервиса мог быть сбой, возможно, есть баг со стороны интеграции или тест действительно нашел проблему нашего приложения. Моки решают эту проблему: когда падают мокированные тесты, скорее всего, причина в баге нашего приложения. При условии стабильности инфраструктуры и соблюдения актуальности моков. Так мы начинаем больше доверять результатам наших тестов. Я собрала самые используемые решения для мокирования в таблицу.
Нам не хватило функций в этих популярных решениях:
И хотелось делать все с помощью одного инструмента. MockingbirdMockingbird — open-source-инструмент для мокирования, созданный Даниилом Смирновым, который работал архитектором в нашей компании. До августа 2023 года работа велась в репозитории корпоративного аккаунта. К сожалению, аккаунт переведен в статус Archived, поэтому развитие сервиса происходит в рамках форка. Инструмент умеет делать эмуляции:
Для решения проблемы с приоритизацией моков есть механизм конфигурации — поле Scope в заглушке. В зависимости от выбранного типа Scope заглушка будет иметь меньший или больший приоритет при поступлении запроса на URL, указанный в заглушке. Таблица с видами Scope, их приоритетами и областью применения
С помощью этого механизма можно создать два мока с разным поведением для одного endpoint: один мок — с конфигурацией Persistent для Happy Path, и мок с конфигурацией Countdown, который будет реализовывать другой сценарий. Mockingbird позволяет создавать моки двумя способами:
Рассмотрим механизмы инструмента при работе с HTTP-сервисами и брокерами сообщений. Эмуляция REST-сервисовИнструмент валидирует тело поступившего на заглушку запроса в режимах:
Можно сравнивать параметры запроса на равенство и неравенство, больше или меньше числа, соответствие регулярному выражению, проверять длину значения и существование поля. Mockingbird поддерживает режимы ответа: raw, json, xml, binary, proxy, json-proxy, xml-proxy. Режимы запроса и ответа независимы, то есть заглушка может ожидать JSON, а отвечать в формате XML. Рассмотрим примеры несложных REST-заглушек. Здесь будет полезен список обязательных полей в HTTP-моке:
Пример 1. Валидация по телу запроса. Для создания мока, который срабатывает на определенное тело, нужно описать условие в блоке request. Пример реализует следующую логику: если запрос, поступивший на эндпойнт с окончанием /stubBody, имеет в теле {“id”: 42}, сработает заглушка. Она отправит статус-код 200 и тело {“field”: “Hello from body trigger mock!”}. В моке используется режим нестрогого соответствия для json — jlens, поэтому для выполнения мока достаточно наличия поля ID со значением 42 в теле запроса. Если бы был выставлен режим строгого соответствия, json, заглушка сработала бы только в случае, если тело запроса было бы строго равно {“id”: 42}. Пример 2. Валидация запроса по query-параметру. Отличие от валидации по телу запроса только в условии срабатывания заглушки. Она срабатывает, если значение query-параметра data, равного 1234, прописано в блоке Query, а не Body и выбран режим No_body, так как мы ожидаем запрос без тела. В ответе мы используем значение поступившего query-параметра, используя конструкцию $query: заглушка вернет имя параметра и его значение. Пример 3. Проксирующий мок. Если не хватает тестовых контуров, хочется иметь возможность тестировать и на реальной интеграции, и на моках. Тогда можно создать мок с режимом Proxy: проксирующий мок перенаправляет запрос на другой ресурс. Это поможет не терять на тестовом контуре работу с живой интеграцией. А в случае, когда хочется работать с моком вместо нее, можно создать мок с Scope = Countdown для наивысшего приоритета при выборе подходящей заглушки. Схематично это будет выглядеть примерно так. Эмуляция шинных сервисовMockingbird взаимодействует с брокерами сообщений через HTTP API, благодаря чему теоретически поддерживаются любые возможные MQ. Наши QA-инженеры работали с RabbitMQ, IBM MQ, Kafka. Для работы с очередями должна существовать очередь в MQ. Mockingbird читает и пишет в нее, используя, как было сказано выше, HTTP API. Поддерживаются следующие режимы для валидации входящего сообщения:
И следующие форматы записи сообщения:
Пример сценария для работы с шинным сервисом. Если в очередь, указанную в source (in_queue), поступит сообщение в формате JSON и с полем innerData.abc со значением test, в очередь, указанную в поле destination (out_queue), отправится сообщение {“xyz”: “abc”} в формате JSON. CallbackЕсли нужно сделать цепочку вызовов — например, сначала получить ответ REST-сервиса, а затем послать сообщение в Kafka или отправить запрос по HTTP, — пригодится механизм Callback. Он позволяет выполнить дополнительное действие после основного. Поведение после получения ответа заглушки описывается в блоке Callback. Тип Callback регулируется полем Type, которое может принимать два значения: HTTP и Message — для работы по HTTP или с брокером сообщений соответственно. Пример с Callback по HTTP. Помимо основных полей заглушки в блоке Callback нужно указать, на какой URL обратиться, метод, тело запроса и заголовки. Пример с Callback в брокер сообщений. В блоке Callback указан тип = Message, очередь, куда нужно положить сообщение (поле Destination), и само сообщение в блоке Payload. < Генерация данныхИногда нужно сгенерировать случайное значение и сохранить или вернуть его в результате работы мока. Это можно сделать с помощью блока Seed, положив туда сгенерированную строку /int/long/uuid/date/dateTime. Для генерации данных используется блок Seed. Для определенного типа данных используется соответствующее указание. Например, для создания случайной строки длиной 20 символов будет использоваться команда %{randomString(20)}, а сгенерированное значение присваивается полю SomeId. Далее сгенерированные значения можно использовать через "$seed.названиеПоля", например "${seed.someId}". < Состояния, stateЕсть сценарии, когда требуется моделировать разные ответы при запросе на один и тот же URL. Для решения подобных ситуаций в Mockingbird используется состояние, или State. Состояние — документ в MongoDB с данными, которые используются в качестве условия для выполнения заглушки. В описании состояния могут присутствовать два блока: State и Persist. State отвечает за чтение, а Persist — за создание и обновление состояния. Логика блоков:
Рассмотрим сценарий: эндпоинт принимает на вход ID карты и возвращает статус готовности: «Одобрено», «Подтверждено» и «Выдано». Нужно создать три мока. Первый будет отвечать статус-кодом 200 и статусом «Одобрено», при этом создаст состояние с помощью блока Persist. В состоянии будет поле _cardId со значением номера карты в запросе и поле Status со значением Approved. Второй мок должен вернуть статус «Подтверждено». В блоке State ищется состояние с _cardId, который совпадает с ID карты из запроса и статусом Approved. Второй мок будет отвечать статус-кодом 200 и статусом «Подтверждено». Блок Persist обновит состояние: поле Status изменится с Approved на Confirmed. Третий мок должен вернуть статус «Выдано». Логика идентична второму моку: поиск State с номером карты, который совпадает с картой из запроса, и Status со значением Confirmed. Блок Persist обновит состояние: в поле Status значение изменится на Issued и отправится статус-код 200 со статусом «Выдано». Помимо подобного сценария State можно использовать для мокирования цепочки вызовов или других ситуаций, где требуется запоминать данные и затем манипулировать ими в других заглушках. Какие заглушки и для чего мы создаемДля ручного тестирования Mockingbird чаще всего используется для создания:
Это позволяет иметь полностью замокированный тестовый контур, на котором можно сэмулировать любой тестовый сценарий. Бизнес-заказчик может пройти основной user-story фичи и проверить ее реализацию. Для автоматизированного тестирования чаще всего используются такие функции:
Заглушка или сценарий создается под конкретный автотест во время прогона, используя API Mockingbird, и удаляются после использования. Применение Mockingbird помогло нам:
В результате команда тестирования легко управляется с актуализацией тестовых сценариев, не переживает из-за флакающих тестов и нестабильности окружающей инфраструктуры. Буду рада, если мой материал поможет в улучшении стабильности тестового окружения. Надеюсь, что сэкономленное время будет использоваться для улучшения процессов вместо выяснения ложных падений тестов. А пользуетесь ли вы инструментами для мокирования, и если да, то какими? |