Использование URLProtocol для имитации сетевых запросов на iOS

Обеспечение стабильной работы UITest может быть сложной задачей, особенно если ваше приложение получает данные с сервера. Основная причина этого заключается в том, что некоторые тесты будут зависеть от сетевого подключения. Эти типы тестов известны как интеграционные тесты и полезны для тестирования данных, которые возвращает нам сервер. Тем не менее, мы должны учитывать следующие вещи…

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

В этой статье мы рассмотрим способ решения этих проблем с помощью насмешек.

Мы напишем небольшой API, чтобы имитировать наши UITests было легко и безболезненно.

Зачем издеваться над сетевыми запросами?

Имитация сетевых запросов в ваших UITests может помочь вам создать предсказуемую среду тестирования. Управляя данными, которые получает ваше приложение, вы можете убедиться, что ваше приложение работает должным образом в различных сценариях.

Представляем URLProtocol

Мы будем использовать старый API, который существует со времен iOS 2 и называется URLProtocol.

Что такое URL-протокол?

  • Он отвечает за загрузку данных URL для конкретного протокола.
  • Это абстрактный тип, который предназначен для подкласса.
  • Он может перехватывать и манипулировать сетевыми ответами на запросы, которые мы делаем в приложении.

Позже мы создадим собственный подкласс URLProtocol для перехвата сетевых вызовов, которые делает наше приложение, а затем вернет настроенный фиктивный ответ.

Издевательство над нашей моделью предметной области

Мы должны имитировать данные ответа на основе наших моделей предметной области, чтобы ответ оставался таким же, как ожидает приложение. Я собираюсь создать вспомогательное расширение нашей модели предметной области, чтобы сделать это проще:

Здесь у нас есть модель предметной области ShoppingList с помощником для создания макета, принимающего массив строк.

Это расширение также полезно для:

  • Повторное использование в наших модульных тестах.
  • Построение предварительных просмотров SwiftUI, которые зависят от этой модели.

Закладка фундамента 🏗

Затем давайте начнем писать наш API для создания фиктивного ответа с заданным путем URL и типом ответа:

Авария:

  1. Мы принимаем путь URL к конечной точке, которую мы хотим имитировать, и тип ответа.
  2. Мы передаем ответ типа ошибка или успешно.
  3. В расширении мы создаем тип MockResponse из нашей модели предметной области ShoppingList.

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

URL-протокол на практике

Теперь мы переопределим упомянутый ранее URLProtocol пользовательской реализацией:

Авария:

  1. Во-первых, мы указываем, какие запросы мы будем перехватывать внутри canInit(with request: URLRequest) -> Bool. Мы храним их внутри словаря. Все остальные запросы обрабатываться не будут.
  2. startLoading() — это место, где мы реализуем логику возврата поддельного ответа.
  3. Сначала мы получаем ответ от allowedRequests, а затем проверяем, относится ли он к типу error или success.
  4. В зависимости от типа мы затем вызываем методы в URLProtocolClient для связи с системой загрузки URL, передавая имитированные данные.

Регистрация UITestConnectionHandler

Чтобы UITestConnectionHandler был распознан системой загрузки URL, нам нужно зарегистрировать его:

Авария:

  1. setMockedResponse() — это просто фасад для установки MockResponse на UITestConnectionHandler.
  2. register() делает наш пользовательский URLProtocol видимым для системы загрузки URL. Это важно вызывать, чтобы все наши сетевые запросы проходили через UITestConnectionHandler .
  3. unregister() делает обратное и удаляет его из системы загрузки URL.

Я создал фасад для настройки имитируемых ответов onUITestConnectionHandler , теперь мы можем объединить весь этот код в отдельный пакет, если захотим.

Запуск кода для UITests в основном приложении

Мы хотим зарегистрироваться UITestConnectionHandler когда запускается наше приложение UITest runner. Поэтому для этого мы создадим расширение ProcessInfo, чтобы определить, запускаем ли мы UITests:

Имея это на месте, теперь мы можем проверить в нашем основном приложении, запускаем ли мы UITests, и если да, то зарегистрируем UITestConnectionHandler:

Хороший! Теперь мы готовы написать наш UITest.

Написание UITest ✅

Когда все готово, мы можем написать наш UITest. Мы настроим фиктивный ответ, используя созданный ранее фасад:

Вуаля! Простой не так ли? И не забывайте, что для добавления другого фиктивного ответа требуется только добавить новый тип MockResponse внутри расширения, которое мы определили ранее.

Заключение

Имитируя сетевые запросы в наших UITests, мы можем создать более стабильную среду тестирования и убедиться, что наше приложение работает должным образом в различных сценариях. Хотя есть и другие способы обеспечить стабильность ваших тестов UITest, URLProtocol — это мощный инструмент, который может помочь вам создавать предсказуемые тесты, не полагаясь на сторонние платформы.

Если у вас есть какие-либо вопросы или вы думаете, что я что-то упустил, оставьте отзыв ниже! Наконец, не забудьте поставить 👏, если вам понравилась эта статья.

Спасибо за прочтение! 👋