Как проверить использование поддельных данных на iOS

Чтобы обеспечить высококачественное программное обеспечение и избежать регрессии, внедрение модульного тестирования является обязательным для каждого приложения iOS.
Пересечение объектов - это метод в модульном тестировании, который создает поддельные объекты, используя те же API, что и реальные.
Эта статья предназначена для того, чтобы предоставить вам лучшие практики использования поддельных данных и написания модульных тестов для самых распространенных сценариев в приложениях для iOS.

При написании юнит-тестов мы всегда должны избегать изменения реальных данных цели приложения и вместо этого использовать поддельные данные только для целей тестирования.

В следующих частях будет обсуждаться, как писать тесты с использованием поддельных данных для часто используемых API-интерфейсов iOS.

Пользователь по умолчанию

В разработке программного обеспечения всегда полезно уменьшать зависимости объектов. Зависимости в лучшем случае должны быть внедрены в классы, которые их используют.

Но если мы проверим реальные сценарии разработки iOS, почти каждый проект использует UserDefaults, вызывая его API напрямую для хранения или извлечения любых данных.

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

Мы можем создать две новые функции на UserDefaults

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

В этом случае мы инициализируем новый объект UserDefaults с помощью suiteName - testDefaults, чтобы он полностью не зависел от стандартных UserDefaults.

Давайте попробуем написать простой тест, который использует UserDefaults

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

Конечно, лучшим местом для очистки этих данных будет функция tearDown в нашем классе модульного тестирования.

Сингелтон Объекты

Объекты Singletons широко используются в iOS во многих API, их можно найти в NSFileManager, NSApplication, UIApplication и во многих других местах.

Знание того, как тестировать одиночные игры, полезно знать разработчикам iOS.

В нашем примере мы будем использовать каркас iAd от apple. Мы создадим файл для получения локального ответа JSON вместо реальных данных при запросе сведений об атрибуции рекламы.

Приятной особенностью iOS является то, что расширения в swift позволяют нам не только добавлять новые функции для предопределенного API, но и заставлять их соответствовать нашим собственным протоколам.

Давайте определим протокол AdvertismentClient

После этого мы по умолчанию используем этот протокол как ADClient, так и наш поддельный рекламный клиент, как показано ниже

Затем мы изменяем зависимость либо

private var adClient: AdvertismentClient = ADClient.shared ()

или

private var adClient: AdvertismentClient = MockAdClient ()

и используйте его следующим образом

Таким образом, мы можем легко решить, когда использовать реальные данные, а когда тестировать, в зависимости от того, проводим ли мы модульное тестирование или вызываем API из нашей целевой прикладной программы.

Основные данные

Базовые данные все еще широко используются в iOS для кэширования данных. Тестирование основных объектов данных может быть сложным. Ниже мы расскажем о хорошей практике организации Core Data Services и Faking Data.

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

Это в основном имеет два преимущества:

  • Он отделяет вас от используемой базовой базы данных, если вы хотите заменить базовые данные любой другой базой данных в будущем, вам придется вносить изменения только в одном классе.
  • Делая это, мы можем легко решить, какой CoreDataStack будет использоваться или какие-либо другие настройки, которые нам могут понадобиться в какой-то другой среде.

Мы создадим протокол CoreDataStack, и после этого два CoreDataStack, которые соответствуют этому протоколу, один MainCoreDataStack и один MockCoreDataStack.

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

Наш основной стек данных будет иметь настройку по умолчанию, как показано ниже

При модульном тестировании всегда следует избегать изменения состояния текущих «реальных» объектов при их тестировании.

Поэтому, когда мы хотим создать поддельные основные объекты данных, у нас должно быть отдельное постоянное хранилище и использовать конфигурацию типа хранилища в памяти, чтобы сделанные изменения не сохранялись на диске, и они были полностью отделены от текущих сохраненных данных.

Теперь мы сможем создать нашу службу базы данных, которая по умолчанию инициализируется с помощью MainCoreDataStack.

И в нашем тестовом классе мы можем инициализировать его с поддельным стеком данных

Теперь мы можем написать несколько простых тестов следующим образом:

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

Сетевые запросы

При тестировании сетевого уровня мы можем использовать подход, ориентированный на протокол, чтобы создать протокол и соответствовать ему как реальным NetworkService, так и MockNetworkService, а затем внедрить зависимости, используя реальный или поддельный сервис.

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

Хорошая вещь об этой библиотеке - то, что она прекрасно работает с известной сетевой библиотекой iOS Alamofire.

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

После этого каждый запрос, отправляемый из приложения по следующему URL, будет возвращать наш пользовательский ответ.

let tasksURL = URL (строка: «https://jsonplaceholder.typicode.com/todos")!

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

Создание словаря для ответа вручную - отличная возможность, но когда мы хотим вернуть большие данные JSON с большим количеством свойств, они могут стать грязными и вряд ли обслуживаемыми в наших тестовых классах.

В этих случаях мы можем использовать JSON-файл, чтобы заглушить ответ, как показано ниже.

Теперь каждый раз, когда наше приложение отправляет запрос, мы получаем ответ из файла myResponse.json, который мы сохранили в наших файлах.

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

Вы можете проверить мою статью на тему безопасности для получения дополнительной информации.

В заключение

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

В этой статье мы обсудили, как обеспечить тестирование для распространенных случаев, возникающих во время разработки iOS.

Мы обсудили, как тестировать UserDefaults, Singeltons, Core Data и сетевые запросы.

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

Следуйте за мной, чтобы просмотреть еще много статей, которые могут поднять ваши навыки iOS разработчика на новый уровень.

Если у вас есть какие-либо вопросы или комментарии, не стесняйтесь оставлять записку здесь или пишите мне на arlindaliu.dev@gmail.com.