Я ходил кругами, пытаясь найти лучший способ модульного тестирования клиентской библиотеки 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
классе. Я вижу следующие варианты:
Я заглушаю
Client
класс и просто проверяю, вызваны ли соответствующие методы. Таким образом, я делаю то же самое, что и выше, но рассматриваю егоClient
как замену API. Это возвращает меня туда, откуда я начал. Еще раз, это дает мне неприятное ощущение от тестирования реализации, а не интерфейса. ЭтоWrapper
вполне может быть реализовано с использованием совершенно другого клиента.Я создаю макет
Client
. Теперь я должен решить, как далеко зайти с его издевательством - создание полного макета сервиса потребует больших усилий (больше работы, чем ушло в саму библиотеку). Сам API прост, но сервис довольно сложен (по сути, это хранилище данных с операциями над этими данными). И опять же, мне придется держать мой макет синхронно с реальнымClient
.Я просто проверяю, что выполняются соответствующие HTTP-запросы. Это означает, что для выполнения этих HTTP-запросов
Wrapper
будет вызываться реальныйClient
объект, поэтому я не проверяю его в отдельности. Это делает его немного ужасным модульным тестом.
Так что я не особенно доволен любым из этих решений. Что бы вы сделали? Есть ли правильный способ пойти по этому поводу?
Ответы:
TLDR : Несмотря на трудности, вы должны заглушить службу и использовать ее для тестирования клиентских модулей.
Я не уверен, что «работа удаленного API-клиента состоит в том, чтобы выполнять определенные вызовы, не более, не менее ...», если только API не состоит только из конечных точек, которые всегда возвращают фиксированный статус и не потребляют и не производят любые данные. Это не самый полезный API ...
Вы также хотели бы убедиться, что клиент не только отправляет правильные запросы, но и правильно обрабатывает содержимое ответов, ошибки, перенаправления и т. Д., А также проверять все эти случаи.
Как вы заметили, у вас должны быть интеграционные тесты, которые охватывают весь стек от wrapper -> client -> service -> DB и далее, но для ответа на ваш главный вопрос, если только у вас нет среды, где интеграционные тесты можно запускать как часть каждого Построение CI без особых проблем (общие тестовые базы данных и т. Д.), Вы должны потратить время на создание заглушки API.
Заглушка позволит вам создать работающую реализацию службы, но без необходимости реализовывать какой-либо уровень ниже самой службы.
Вы можете рассмотреть возможность использования решения на основе DI для реализации этого с реализацией шаблона Repository под ресурсами REST:
В любом случае, если озвучивание и / или рефакторинг сервиса не обсуждается ни политически, ни практически, я бы, вероятно, предпринял нечто подобное вышеперечисленному. Если это невозможно, я бы позаботился о том, чтобы обеспечить действительно хорошее интеграционное покрытие, и запускал бы их так часто, как это возможно, учитывая ваши доступные настройки тестирования.
НТН
источник