В этом посте мы рассмотрим, как протоколы и внедрение зависимостей могут помочь нам в написании чистого тестируемого кода на Swift.

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

Но если у вас есть API, который добавляет число к глобальной переменной и принимает только один аргумент, это не чисто. Его ответ зависит от значения глобальной переменной и аргументов. Предположим, что этот API находится в классе Accumulator, а глобальная переменная, которую мы определили ранее, была свойством этого класса - тогда класс чистый, но API все еще нет.

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

В этом посте мы можем попытаться сделать нечистый класс и функцию чистыми, тем самым тестируемыми 🤘🏻

Пример класса пользователя

Тестирование класса пользователя

Мы собираемся протестировать класс User, который мы только что написали. Единственная цель класса на данный момент - получить изображение профиля из какой-либо службы, помимо сохранения отображаемого имени.

Но если вы заметили API fetchProfileImage, он тесно связан с классом ImageDownloader в API, что делает его нечистым. Как только мы вызываем этот общедоступный API, мы теряем возможность контролировать сетевые вызовы, которые ImageDownloader выполняет для получения изображения.

Чтобы исправить это, давайте немного перепишем API fetchProfileImage.

Основное изменение здесь - введение загрузчика в качестве параметра API. Интересно, как это поможет нам в тестировании API?

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

Давайте посмотрим на пример:

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

Это называется внедрение зависимостей!

Использование протоколов

Давайте посмотрим, как мы можем снова улучшить этот код. В настоящее время наш API fetchProfileImage принимает конкретный тип ImageDownloader. Давайте изменим его, чтобы принять протокол ImageFetcher.

Вы можете прочитать их, если не знакомы с протоколами:





Давай сделаем это:

Это очень незначительное изменение: мы заменили класс ImageDownloader на интерфейс ImageFetcher. Прелесть ImageFetcher в том, что он может получать изображение из любого источника в зависимости от реализации конкретного объекта, который мы передаем.

Конечная реализация класса пользователя

Тестирование

Вот как мы можем протестировать приведенный выше код:

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

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

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



Дайте мне знать, как вы использовали эти приемы для съемки репортажей в своих проектах 😉