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

Написать собственное представление довольно просто. Вы можете, например, расширить android.view.View или расширить свой любимый макет, например FrameLayout, и расширить XML для своего пользовательского представления. Вместо того, чтобы включать всю иерархию представлений в файлы макета, все, что вам нужно сделать, это включить настраиваемое представление и использовать его.

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

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

Однако, поскольку на данный момент наш настраиваемый класс представления просто расширяет android.view.View или макет, практического способа сделать это нет. К счастью, такие архитектуры, как MVP и MVVM, не обязательно должны использоваться исключительно с Activity и Fragment. Так же легко реализовать эти архитектуры в настраиваемых представлениях, чтобы отделить логику от самого представления, в результате чего получится уровень логики, тестируемый модулем и уровень представления, проверяемый пользовательским интерфейсом.

Давайте подумаем о настраиваемом представлении, которое используется для отображения текущего состояния пользователя. Предполагается, что в представлении будет отображаться изображение (аватар пользователя) и его имя пользователя.

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

Идентифицированные состояния просмотра:

  • Вышли из
  • Выполнен вход (подтверждено, ожидает подтверждения, требуется подтверждение)

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

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

В пользовательском представлении barebones мы должны передать имя, аватар и активированное поле (с возможностью установить для представления состояние выхода из системы).

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

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

Определить нашу модель довольно просто: Учетная запись.

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

  • TextView для имени пользователя / текста после выхода из системы
  • ImageView для аватара пользователя / аватара, вышедшего из системы
  • ImageView, чтобы показать активированное / ожидающее состояние

Для каждого из этих возможных состояний мы создадим метод в нашем интерфейсе просмотра:

Наш Presenter будет довольно простым, все, что он делает, - это заботится о жизненном цикле представления (подписывается на источники данных и обрабатывает их удаление путем отказа от подписки):

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

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

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

Для наших модульных тестов мы будем использовать следующие зависимости:

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

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

Неполный список возможных вещей для тестирования (полный список находится в репозитории):

  • вышедший из системы аккаунт скрывает значок
  • активная учетная запись устанавливает имя
  • активная учетная запись устанавливает аватар
  • активная учетная запись с ожидающей активацией показывает значок

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

Реализация этих тестов может выглядеть так:

Заметить, что:

  • Все, кроме нашей тестовой мишени (ведущего), является фиктивным.
  • Докладчик использует источник данных для получения данных, которыми мы можем манипулировать для запуска различных состояний (вход в систему, выход из системы,…)
  • Каждый тест проверяет одно поведение для указанного набора условий.

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

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

Для этого вы можете добавить mock вариант сборки к приложениям, которые содержат утилиты для тестирования представлений (и действий / фрагментов). Сделать это довольно просто. Просто измените build.gradle вашего модуля и добавьте вкус.

В этом примере вариант mock будет использоваться для хранения ложных источников данных, а также наших тестовых утилит. Вариант prod (продакшн) будет содержать «настоящие» реализации. Разделение их с помощью разновидностей дает большое преимущество: ни один из ваших фиктивных кодов не появится в скомпилированном двоичном файле вашего производственного приложения - это отдельная среда.

Источник данных нашего представления предоставляет нам учетную запись пользователя:

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

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

Все готово, теперь пора наконец протестировать наш пользовательский режим просмотра. Мы создадим класс в нашем androidTest каталоге и настроим ActivityTestRule для нашего MockActivity.

Наш MockActivity полагается на то, что мы предоставим идентификатор ресурса макета, который затем будет использоваться для настройки представления содержимого Activity. В нашем тесте мы просто обернем наше пользовательское представление в макет. XML-файл создается в папке ресурсов макета версии.

Возвращаясь к нашему AccountViewTest, теперь мы установим идентификатор ресурса макета MockActivity на наш недавно созданный:

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

Пример одного теста пользовательского интерфейса Espresso с нашим MockActivity, имитируемым источником данных и нашим пользовательским представлением в файле макета XML только для имитации:

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

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

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

Ниже вы найдете ссылку на репозиторий GitHub, который я создал, с полным примером настраиваемого представления с модульными и пользовательскими тестами.



Если вам понравился этот пост, хотите его обсудить или просто оставаться в курсе - подписывайтесь на меня в twitter: