Лучший подход к написанию тестов далеко не очевиден

На заре мобильной разработки клиентское тестирование в основном вращалось вокруг тестирования пользовательского интерфейса. Apple представила технологию под названием UIAutomation, которую она полностью устарела в 2016 году с выпуском Xcode 8. Репозиторий тестовых образцов Android не создавался до 2014 года.

Из-за нисходящего подхода, необходимого для тестирования пользовательского интерфейса, тесты сломаются, если вы значительно измените дизайн своего пользовательского интерфейса. Кроме того, тестирование пользовательского интерфейса само по себе не обрабатывает различные логические состояния, возникающие из зависимых систем, такие как реакции на сбой сети. Подтверждение того, что кнопка меняет цвет при нажатии, часто менее ценно, чем подтверждение правильности ответа приложения при получении ответа 500 от сервера. Для тестирования пользовательского интерфейса также требуется эмулятор для запуска, который часто занимает десятки секунд для запуска на ноутбуке и даже дольше при запуске по сети (например, с такими сервисами, как AWS Device Farm).

Хотя в конечном итоге Android выпустила Espresso, а Apple выпустила XCTest, наш подход к тестированию в чистой архитектуре MVVM лишает приоритета тестирования пользовательского интерфейса, вместо этого сосредотачиваясь на тестировании на уровне ViewModel ниже.

Обычно мы пишем тесты сначала для репозиториев, затем для ViewModels и, если необходимо (поскольку уровень ViewModel не выполнял все условия), уровень логики.

Сосредоточив сначала тестирование на ViewModel, можно очень быстро запускать тесты, не полагаясь на эмулятор. В Android мы достигли этого с помощью локальных модульных тестов, которые выполняются на локальной виртуальной машине Java вашего компьютера и не имеют зависимостей от платформы Android. В iOS мы находимся в процессе перемещения классов ViewModel, Logic и Repository в Swift Packages.

Внедрение зависимости для победы

Независимо от того, проводите ли вы тестирование BDD или TDD, вы захотите рассмотреть возможность использования внедрения зависимостей для своих тестов. Если вы этого не сделаете, то в начале каждого теста вам нужно будет вызвать метод (может быть назван как-то вроде buildContext), который будет эффективно создавать все необходимые классы ViewModel, Logic и Repository, тем самым действуя как облегченный Система DI.

Вот базовое использование системы DI:

А вот как мы бы настроили тесты в начале тестового файла:

А как насчет моков?

Одним из ключевых преимуществ использования архитектуры Clean MVVM является то, что, если вы следовали шаблону, вам нужно только когда-либо имитировать чистые компоненты внешнего уровня - все API и, возможно, классы локального хранилища.

С mockito очень просто насмехаться над Android. Вот как вы можете определить макет API в Koin, внедрить его, а затем переопределить свой макет в начале теста:

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

Лучшая структура с BDD

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

Как только ваше приложение достигнет заданной степени сложности, может потребоваться значительная начальная настройка в тесте для достижения состояния, которое вы пытаетесь оценить. Чтобы очистить наши тесты и уменьшить избыточный код, мы можем написать тесты BDD, которые используют вложение кода для повторного использования блоков инициализации и настройки. Мы можем использовать JUnit в Java и Quick в Swift, чтобы получить предметно-ориентированные языки, которые помогут нам писать более короткие тесты в стиле BDD.

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

Вот те же тесты, структурированные в BDD с использованием Junit:

Следующий

Поговорим о том, как мы тестируем асинхронность в Swift и Kotlin.

Больше в этой серии

Другие серии, которые могут вам понравиться

Чистая архитектура API (2021 г.)
Классы, шаблоны выполнения и абстракции при построении современной конечной точки API.

Жизненный цикл активности Android считается вредоносным (2021 г.)
Смерть процесса Android, необъяснимые исключения NullPointerExceptions и жизненный цикл MVVM, который вам нужен прямо сейчас

Об авторе

Эрик Сильверберг - генеральный директор Perry Street Software, издателя приложений для знакомств LGBTQ + SCRUFF и Jack’d, насчитывающих более 20 миллионов пользователей по всему миру.