Имитация API на C# с WireMock.Net |
14.12.2022 00:00 |
Автор: Баз Дейкстра (Bas Dijkstra) Если вы давно читаете мои статьи, то, возможно, знаете, что я большой поклонник WireMock, сервиса с открытым исходным кодом на Java, созданным для имитации API и виртуализации служб. Я даже создал и опубликовал бесплатный воркшоп с открытым исходным кодом по этому инструменту. Лишь недавно, готовя курс по тестированию API на C# для заказчика, я узнал, что WireMock также портирован на C#. В этой статье я хочу пристальнее рассмотреть WireMock.Net, как (предсказуемо) называется эта библиотека. Прежде чем начать, сообщу, что честь создания и поддержки библиотеки принадлежит Штефу Хайнрату. Я не буду глубоко нырять в сценарии использования WireMock.Net, так как ее задачи абсолютно идентичны "оригинальному" WireMock: позволить вам имитировать внутренний или внешний API, дабы писать более эффективные тесты и тестировать раньше, больше и чаще. Это также не исчерпывающий обзор всех функций WireMock.Net – скорее коллекция примеров, которые я считаю наиболее важными, или мощных возможностей. Определение и тестирование первого ответа имитатора API После добавления WireMock.Net в проект с использованием пакета NuGet имитировать вызов API в тесте при помощи WireMock.Net можно следующим образом: private WireMockServer server; [SetUp] private void CreateHelloWorldStub() [TearDown] Затем мы можем написать тест для этого имитатора API с RestSharp, например: private RestClient client; [Test] // Определяется HTTP-запрос для отправки RestRequest request = new RestRequest("/hello-world", Method.Get); // HTTP-запрос отправляется на сервер-имитатор // Проверка, что сервер-имитатор возвращает ответ с ожидаемыми свойствами Этот тест пройдет успешно, говоря нам, что наш имитатор API работает согласно ожиданиям. Теперь, разобравшись, как работает WireMock.Net, посмотрим на ключевые функции, которые я всегда ищу во всех инструментах и библиотеках имитации API, и посмотрим, как они внедрены тут. Стратегии совпадения запросов В первую очередь в инструментах имитации API я изучаю их способности в отношении совпадения запросов, или идентификации определенных характеристик запросов, чтобы выбрать подходящий ответ. Как упомянуто в документации по совпадению запросов, WireMock.Net может сравнивать запросы на основании пути или URL, метода HTTP, параметров запроса, заголовков, куки и тела. В примере выше наличествует две из этих стратегий – URL и метод HTTP. Имитация отвечает только на вызовы HTTP GET к конечной точке /hello-world. Посмотрим на ряд других примеров и начнем со сравнения запросов, которые содержат (или не содержат) определенный заголовок с определенным значением. Это очень полезно для, к примеру, работы с механизмами аутентификации API. private void CreateStubHeaderMatching() server.Given( Помимо ExactMatcher, проверяющего точность совпадения значений, есть и другие полезные обнаружители совпадений, включая различные мэтчеры JSON и regex. Мы также можем проверять совпадения элементов JSON-тела запроса, например, так: private void CreateStubRequestBodyMatching() server.Given( Вернется совпадение с этим телом запроса: { "cars": [ { "make": "Alfa Romeo" }, { "make": "Lancia" } ] } Симуляция проблем и задержек Одно из преимуществ использования имитаторов перед работой с реальными зависимостями – это тестирование устойчивости системы к проблемному или неожиданному поведению этой зависимости. Так как задержки и ошибки зачастую сложно, если вообще возможно, спровоцировать в реальности, это поведение имеет смысл моделировать в имитаторе. Помимо возвращения кодов HTTP статуса 4хх или 5хх, что делается путем передачи правильного значения в метод WithStatusCode() при создании ответа, WireMock.Net также поддерживает другие типы неожиданного поведения. Вот пример, возвращающий валидный ответ, но с предустановленной задержкой: private void CreateStubReturningDelayedResponse() server.Given( Использование WithDelay() позволяет моделировать поведение производительности и проверить, что таймауты и задержка ответов правильно обрабатываются тестируемым приложением. WireMock.Net также дает возможность возвращать "плохие" ответы: private void CreateStubReturningFault() server.Given( Создание заглушек, сохраняющих состояния Еще одна полезная функция имитатора – это способность имитировать хранение состояний ("память"). Иногда вам нужно моделировать поведение с сохранением состояния, то есть поведение, при котором важен порядок поступающих запросов. WireMock.Net поддерживает это через создание конечных автоматов (FSM) – коллекций состояний и переходов. private void CreateStatefulStub() server.Given( server.Given( server.Given( HTTP GET запрос к /todo/items, пришедший перед POST-запросом к той же конечной точке, вернет список дел с единственным пунктом "Buy milk" внутри, в то время как тот же самый GET-вызов, полученный после POST, вернет список дел, содержащий как "Buy milk", так и "Cancel newspaper subscription". Шаблоны ответов Последняя функция, которую я обычно исследую, изучая имитатор API – это способность повторно использовать значения из входящего запроса в соответствующем ответе. Это полезно при работе со значениями, необходимыми для успешной обработки ответа на стороне потребителя, которые невозможно заранее предсказать. К примеру, это транзакционные или сессионные ID, куки, и другие подобные значения. Как и Java-коллега, WireMock.Net поддерживает переиспользование значений запроса через создание шаблонов ответов. Вот пример, исследующий запрос, извлекающий используемый HTTP-метод и указывающий его в теле ответа: private void CreateStubEchoHttpMethod() server.Given( Метод WithTransformer() нужно добавлять к определению ответа, в противном случае тело ответа будет дословно содержать значение '', и шаблон ответа не будет применен. К сожалению, вроде бы нет способа применить это глобально (как можно сделать в WireMock на Java, поэтому это нужно добавлять к каждому определению ответа). Вот еще пример, использующий шаблон ответа – на этот раз для извлечения значения из JSON-тела запроса, чтобы использовать его в текстовом теле ответа: private void CreateStubEchoJsonRequestElement() server.Given( Как видно, WireMock.Net позволяет извлекать значения элементов из (JSON) тела запроса, используя выражения JsonPath, и повторно использовать их – к примеру, для повтора в теле ответа. На этом я завершаю первый обзор WireMock.Net. Повторюсь, это, безусловно, далеко не полный обзор всех ее возможностей – лишь краткий перечень функций, которые я считаю наиболее ценными и интересными. Меня приятно удивила простота использования – хотя, надо признать, у меня было несправедливое преимущество, так как я несколько лет работал с WireMock на Java. Предвкушаю дальнейшее изучение WireMock.Net. Если вы хотите узнать об этой библиотеке больше, я рекомендую документацию. Все примеры из статьи я загрузил на GitHub вместе с NUnit + RestSharp-тестами для демонстрации, что имитируемые ответы API работают верно. |