Модульное тестирование клиента API и оболочек

14

Я ходил кругами, пытаясь найти лучший способ модульного тестирования клиентской библиотеки API, которую я разрабатываю. Библиотека имеет Clientкласс, который в основном имеет отображение 1: 1 с API, и дополнительный Wrapperкласс, который обеспечивает более удобный интерфейс поверх Client.

Wrapper --> Client --> External API

Сначала я написал несколько тестов для обоих Clientи Wrapper, фактически, просто для проверки того, что они перенаправляют к соответствующим функциям того, над чем работают ( Wrapperработают Clientи Clientработают по HTTP-соединению). Однако мне стало не по себе от этого, потому что я чувствую, что тестирую реализацию этих классов, а не интерфейс. Теоретически, я мог бы изменить классы, чтобы иметь другую совершенно правильную реализацию, но мои тесты не пройдут, потому что функции, которые я ожидал вызвать, не вызываются. Это звучит как хрупкие испытания для меня.

После этого я подумал об интерфейсе занятий. Тесты должны проверить, что классы действительно выполняют ту работу, для которой они предназначены, а не то, как они это делают. Так как я могу это сделать? Первое, что приходит на ум, - это заглушка запросов внешнего API. Однако я нервничаю по поводу упрощения внешнего сервиса. Множество примеров API-заглушек, которые я видел, просто дают постоянные ответы, что звучит как очень простой способ проверить, что ваш код работает правильно против вашего поддельного API. Альтернатива - издеваться над сервисом, который просто невозможен, и его необходимо постоянно обновлять всякий раз, когда реальный сервис меняется - это похоже на излишнее количество и трата времени.

Наконец, я прочитал это из другого ответа на программистов SE :

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

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

Тем не менее, я все еще застрял в Wrapperклассе. Я вижу следующие варианты:

  1. Я заглушаю Clientкласс и просто проверяю, вызваны ли соответствующие методы. Таким образом, я делаю то же самое, что и выше, но рассматриваю его Clientкак замену API. Это возвращает меня туда, откуда я начал. Еще раз, это дает мне неприятное ощущение от тестирования реализации, а не интерфейса. Это Wrapperвполне может быть реализовано с использованием совершенно другого клиента.

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

  3. Я просто проверяю, что выполняются соответствующие HTTP-запросы. Это означает, что для выполнения этих HTTP-запросов Wrapperбудет вызываться реальный Clientобъект, поэтому я не проверяю его в отдельности. Это делает его немного ужасным модульным тестом.

Так что я не особенно доволен любым из этих решений. Что бы вы сделали? Есть ли правильный способ пойти по этому поводу?

Джозеф Мэнсфилд
источник
Я склонен избегать модульного тестирования в этих сценариях, где большая часть тяжелой работы выполняется сторонней библиотекой, а у меня просто есть обертка (в основном потому, что я не знаю, как сделать это таким образом, чтобы проверять что-либо действительно значимое). Обычно я делаю интеграционное тестирование в этих случаях, возможно, с помощью фиктивного сервиса. Может быть, кто-то знает, как сделать действительно значимый модульный тест для них - я склонен расставлять приоритеты для наиболее важных компонентов системы, находящихся под нашим контролем. Здесь критическая часть находится вне нашего контроля. :-(

Ответы:

10

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


Я не уверен, что «работа удаленного API-клиента состоит в том, чтобы выполнять определенные вызовы, не более, не менее ...», если только API не состоит только из конечных точек, которые всегда возвращают фиксированный статус и не потребляют и не производят любые данные. Это не самый полезный API ...

Вы также хотели бы убедиться, что клиент не только отправляет правильные запросы, но и правильно обрабатывает содержимое ответов, ошибки, перенаправления и т. Д., А также проверять все эти случаи.

Как вы заметили, у вас должны быть интеграционные тесты, которые охватывают весь стек от wrapper -> client -> service -> DB и далее, но для ответа на ваш главный вопрос, если только у вас нет среды, где интеграционные тесты можно запускать как часть каждого Построение CI без особых проблем (общие тестовые базы данных и т. Д.), Вы должны потратить время на создание заглушки API.

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

Вы можете рассмотреть возможность использования решения на основе DI для реализации этого с реализацией шаблона Repository под ресурсами REST:

  • Замените весь функциональный код в обработчиках REST вызовами IWhwhatRepository.
  • Создайте ProductionWhwhatRepository с кодом, извлеченным из ресурсов REST, и TestWhwhatRespository, который возвращает готовые ответы для использования во время модульного тестирования.
  • Используйте контейнер DI для внедрения либо DLL-библиотеки / класса ProductionWhwhatRepository, либо TestWhwhatRepository и т. Д., В зависимости от конфигурации.

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

НТН

Dan1701
источник