У любого разработчика или даже программы возникает потребность в интеграции с другой программой или микросервисом. Возьмем как частность интеграцию через REST интерфейс. Хорошо если у поставщика есть тестовый API для периода разработки и/или ручного тестирования. А вдруг его нет, или еще не готов, или нужно написать автотесты для адаптера. Что ж в этом случае нам поможет MMock сервер
Знакомьтесь это Игорь — 👦🏻 программист. Один его знакомый продает на своем сайте печеньки. Бизнес идет вверх, покупателей все больше. Он решил перенести общение с покупателями и слежение за заказами в популярную CRM и попросил Игоря создать модуль для нее. Игорь с энтузиазмом взялся за это дело. Так как он любит писать тесты, то решил полностью покрыт модуль тестами. И как раз, он услышал об одном инструменте для мокирования внешних сервисов и решил попробовать.
Мок? Что?
Для начала разберемся — Что такое Мок? Объяснение по научному
Mock-объект представляет собой конкретную фиктивную реализацию интерфейса
Объяснение попроще:
Это имитация какого либо обьекта, мимикрирование под него. Мимикриды из игры Prey могли мокировать обьект стул или например персонаж Рианны из фильма «Валериан и тысяча планет» могла мокать обьект под названием человек.
С терминами разобрались. Насчет самого Mmock — это отличный инструмент для мокирования HTTP запросов. Запускается как сервер слушающий входящие запросы на порту 8083. Сервер написан на Golang, маленький и быстрый, основной код находится здесь https://github.com/jmartin82/mmock
Установка и Запуск
Есть два вида установки, в первом случае понадобиться установленный и настроенный golang, второй способ через Docker. Рекомендую устанавливать через Docker.
1 2 3 |
docker run -v YOUR_ABS_PATH:/config -p 8082:8082 -p 8083:8083 jordimartin/mmock |
YOUR_ABS_PATH — путь к конфигам. Конфиги создаются в формате json or yaml, описывают в декларативном стиле какой запрос ожидается и какой ответ должен быть на этот запрос.
После запуска нас приветствует веселый лягушонок
GET запрос
Пример, получение пользователя по его ID через REST API. Все примеры лежат на GitHub — https://github.com/Carsak/mmock-test
Файл GetUserById.yaml
1 2 3 4 5 6 7 8 9 |
description: 'Get User By Id' request: method: GET path: "/user/99" response: body: '{"email" : "alma@alma.kz", "id" : 99, "username" : "alma_z"}' statusCode: 200 |
Запрос http://localhost:8083/user/99 вернет готовый ответ в виде
Можно воспользоваться переменными из запроса, файл GetUserByVarId.yaml
1 2 3 4 5 6 7 8 9 |
description: 'Get User By Id' request: method: GET path: "/user/var/:id" response: body: '{"email" : "alma@alma.kz", "id" : {{ request.path.id }}, "username" : "alma_z"}' statusCode: 200 |
Тогда ответ body.id будет зависеть от :id отправленного в запросе
Если что то пошло не так? У Mmock есть отличный веб интерфейс для просмотра истории запросов, ошибок, просмотра списка загруженных конфигов, создания конфига, редактирования конфига. Получить доступ к нему можно по адресу http://localhost:8082/
Как отправить POST Запрос?
Например, создание заказа в CRM системе, в метод order/create/ отправляется данные с заказом. При успешном создании метод возвращает код 200 и ИД созданного заказа. При неуспешном код ответа 400, возможно с текстом ошибки.
Конфиг файл будет выглядеть так:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
description: "Create Order POST" request: method: POST path: /order/create/ body: '{"email": "alma@alma.com", "product": "choco-cookies", "quantity" : 3}' response: headers: Content-Type: - application/json statusCode: 200 body: '{"status": "ok", "id" : "{{ fake.UUID }}"}' |
Отправить запрос можно любым HTTP клиентом или через CURL
1 2 3 4 5 6 |
POST http://localhost:8083/order/create/ Content-Type: application/json {"email" : "alma@alma.kz", "product" : "choco-cookies", "quantity" : 3} |
на что придёт ответ
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "statusCode": 200, "headers": { "Content-Type": [ "application/json" ] }, "cookies": null, "body": "{\"status\": \"ok\", \"id\" : \"e0487e5f-f796-4362-bba8-b2fe28f0533d\"}" } |
Обратите внимание, ID был сформирован самим Mmock с помошью fake.UUID
В теле запроса, судя по документации, должен работать паттерн «*», но получить коррекный ответ с ним не получилось. Был создан баг репорт на гитхабе https://github.com/jmartin82/mmock/issues/96
В ответе также можно получить доступ к свойствам body запроса через dot нотацию — request.body.key, например
1 2 3 |
body: '{"status": "ok", "id" : "{{ fake.UUID }}", "email" : "{{ request.body.email }}"}' |
Написание модуля идет полным ходом, Игорь внедряет новый инструмент в работу. Половину тестов он уже написал. Остались интеграционные тесты связанные с авторизацией. Он задался вопросом : «Можно ли с помощью MMock сымитировать сохранение авторизации?»
Сохранение состояния STATEFUL REST
В Mmock есть возможность изменить состояние через Scenarios
Схема выглядит так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
+---------------------------------------------------------------------------------------------+ | | | GET /user POST /user GET /user | | StatusCode: 404 StatusCode: 201 StatusCode: 200 | | | | +-------------------------+ +---------------------------+ +-------------------------+ | | | | | | | | | | | requiredState:created +-> | requiredState:not_started +-> | requiredState: created | | | | | | newState: created | | | | | | | | | | | | | +-------------------------+ +---------------------------+ +-------------------------+ | | | +---------------------------------------------------------------------------------------------+ |
Пример. Купить товар может только авторизованный пользователь. Прежде чем метод order/create станет доступным, нужно авторизоваться user/login, затем после покупки пользователь может выйти user/logout.
Конфиги можно посмотреть в папке https://github.com/Carsak/mmock-test/tree/master/config/scenario
requiredState — состояние до запроса, newState — состояние после запроса
На выходе получаем выполнение сценария:
Игорь теперь очень доволен. Он воспользовался новым инструментом Mmock, покрыл тестами весь модуль и может спать спокойно по ночам, не волнуясь за свой код. А заказчик рад, что интеграция с CRM прошла успешна и теперь может быстрее обслуживать клиентов, продавать больше печенек.
Заключение
Mmock можно использовать для тестов, чтобы не зависеть от внешних сервисов, исключить их из тестов как точку отказа.
Также он может пригодиться при разработке. Если нужно создать прототип API, с которым код должен взаимодействовать.
Можно было бы конечно использовать заглушки. Но заглушки используются на уровне кода, а Mmock работает на 7 уровне модели OSI, то есть на прикладном уровне, протоколу HTTP. (Так что можете смело использовать библиотеки типа Guzzle.) Что дает более глубокое покрытие тестами.