UI-tests in Android. Часть 2. Простые примеры.

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

Немного о проекте — что это и зачем. Году так в 2004 если не ошибаюсь, был у меня компьютер пентиум 4. И еще тогда у меня был интерес к изучению иностранных языков. Помню тогда приобрел программу ABBYY Lingvo 10. На 2 CD. Если кто не знает, это просто словарь. Ты вводил слово на английском или русском и получал перевод. Но кроме основной программы, там был еще и так называемый помощник — Tutor. Его интерфейс выглядел именно так. Есть слово, тебе нужно ввести перевод. Все. Слова для проверки ты брал из словаря или же руками вбивал. Таким образом я в свое время пополнял свой словарный запас.

Сейчас же я написал свой проект, который так же назвал Tutor (link). Суть его точно такая же — добавить в некий словарь свои слова для изучения и потом изучать их. Теперь посмотрим каким образом мне помогали в этом деле юайтесты.

Напомню — Первоочередная цель и миссия юай тестов — проверка функционала!

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

Главный экран приложения Tutor

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

Это и есть первый тест-кейс. То есть что такое тест кейс? Это поведение приложения при каких-то условиях. Наш тест кейс будет звучать примерно так —

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

Теперь. Нам как писателям юайтестов вообще неважно какой там код. Мы смотрим на юай и на ресурсы.

Вся суть юай тестов

Если в юнит тестах мы писали проверки типа — assertThat(actual, is(expected)) то в юай тестах наша структура следующая (используем Espresso, кстати оно в градле уже по дефолту есть, ничего не надо добавлять в проект).

onView(VIEWMATCHER).VIEWASSERTION(VIEWACTION/VIEWMATCHER)

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

Большая часть проверок реализована с помощью экстеншн функций Котлина. Это очень удобно, мы позже увидим. Самое простое что мы можем проверить, что вьюшка с каким-то айди видима на экране. Для этого пишем такой простой код —

fun Int.checkViewIsVisible() = onView(withId(this)).check(matches(isDisplayed()))

Перевожу на русский — найти вью с таким айди, проверить, что она видима. Все. Элементарно, правда?

Так же у нас есть возможность проверить, что вью невидима и что ее просто не существует на экране. Также для нашего тест кейса нам нужен метод проверки отображения сообщения. Так как наше сообщение это Toast, то напишем метод проверки checkToast. Очень важно понимать следующую вещь, мы проверяем текст сообщения, какой он есть, а не проверяем что отобразился ресурс с таким-то айди. Почему? Потому что завтра возможно кто-то (например вы) поменяете текст этого ресурса, а чтобы не забыть, что вы использовали такой текст уже где-то, у вас есть юай тест.

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

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

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

Итак, это был первый и самый простой юай тест, на который уходит меньше 1 секунды. Стоит ли оно того, чтобы написать его? Мне кажется что да. Ведь вы всегда будете уверены в том, что ваш функционал работает как нужно. Здесь нужно отметить, что у нас в юай тесте есть 2 функции как и в юнит тестах — Before & After. Методы, которые запускаются до теста и после. Нам в нашем тесте нужно удовлетворить условие — мы находимся в состоянии первоначальном, когда нет ни одной записи в базе данных. Для этого перед запуском нашего теста мы будем очищать нашу базу данных (смотрите BaseTest#setUp).

Итак, идем дальше. Мы проверили что первый видимый экран, который имеет немного функционала работает по вложенной в нее логике для первоначального состояния. Теперь мы должны проверить первую главную кнопку — добавление нового элемента. Для этого я написал 8 классов тестов — их можно посмотреть по ссылке.

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

Экран добавления новой пары для изучения

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

Так как я не хочу, чтобы все изучаемы пары клались в одно и то же место, я придумал так называемые категории. В начальном состоянии нам нужно создать категорию, так как нет никаких предустановленных. Значит, у меня будет радиобатоны для выбора категории — создать новую или же выбрать из списка существующих. Чтобы было понятно, тогда когда у нас нет существующих категорий у нас недоступна для нажатия радиокнопка (я решил что так правильно, но я не UI/UX expert, так что могу ошибаться). Теперь, что же будет, когда у нас будет хотя б 1 категория? Тогда кнопка выбора будет доступна для нажатия и при нажатии мы увидим вместо поля ввода имени категории выпадающий список существующих категорий. Чтобы было совсем понятно, вот вам ссылка на разметку этого экрана.

Но будем последовательны. Первый тест кейс который напрашивается на ум — проверить поля ввода на валидность. Да, у нас есть проверка введенных данных — и она на самом деле проста до невозможности — наше требование просто чтобы было введено хотя бы по 1 символу в поля для источника и перевода (кроме конечно же пробелов) и чтобы был введен хотя бы 1 символ для имени новой категории если мы создаем новую категорию, или же был выбран пункт существующей категории. Код можно посмотреть здесь (81 Line).

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

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

Теперь, вот вам ссылка на сам тест. Опять же мы создадим пейдж обьект для этого экрана (ссылка). В ней будем хранить все айди вьюх и тексты сообщений. Ну и после чего напишем наш тест — как видим он достаточно длинный, но опять же меньше 100 строк. Зато он дает нам уверенность в том, что невалидные данные не будут сохранены в базу данных. И на это все уходит всего лишь 16 секунд. Достаточно мало, неправда ли?

Рассмотрим какие методы мы здесь использовали — проверка видимости и невидимости вьюх. Тайпинг в поля ввода — здесь давайте сделаем акцент — посмотрим на метод.

fun Int.typeText(text: String) = 
onView(withId(this)).perform(
    clearText(), 
    ViewActions.typeText(text),
    closeSoftKeyboard()
)

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

Теперь, давайте пробежимся быстро по всем существующим в пакете юайтестам.

AddNewPairAlertDialogTest — проверка окна подтверждения ухода с экрана добавления. Здесь все довольно просто — нужно отменить создание нового элемента и проверить, что ничего не добавилось.

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

AddNewPairExistingCategoryTest — создаем программно 2 категории, выбираем из выпадающей вью вторую и добавляем туда новый элемент, после чего проверяем, что он туда добавился. Здесь мы проверяем функционал добавления айтема в сущестующую категорию причем когда категорий не одна. Т.е. выбор нужной категории.

AddNewPairNewCategoryTest — проверка основного функционала — добавление нового элемента в новую категорию.

А здесь давайте сделаем паузу и порассуждаем. Вот у нас есть поле ввода для новой категории. А что случится, если мы в это поле ввода напишем имя сущестующей категории? Наши элементы все уникальны и их уникальность гарантируется их именем. Т.е. у вас не может быть 2 разные категории в каждой из которых есть слово Hello с разными или одинаковыми переводами. Также сами категории уникальны и их уникальность обеспечивается их именем.

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

AddNewPairReplaceExistingWithNewCategoryTest — Создаем новый элемент в новой категории, после пытаемся создать этот же элемент для другой категории, заменяем существующий и проверяем, что теперь у нас в списке категорий есть 2 значения. Первый с количеством элементов 0, а второй с количеством элементов 1. Где лежит наш уже созданный элемент.

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

Кто-то скажет, что все эти тесты такие простые, Оганнес, давай что-нибудь действительно интересное. Ок. Мы все знаем, что в андроид есть такой момент как смерть процесса, который происходит когда система решает, что ваш процесс перестал быть суперважным и его можно прибить. А после этого, когда юзер вернется восстановить данные. И это можно протестировать только если в настройках разработчка поставить галку на Не хранить активити(Don`t keep activities) и количество фоновых процессов 0. Так как в моем проекте нет никакой библиотеки для навигации — я написал все старым добрым фрагмент менеджером, значит по дефолту после смерти процесса мы должны увидеть приложение в первоначальном состоянии. Т.е. если вы были на экране добавления новых элементов, то после смерти процесса ваши введенные данные потеряются и вы увидите экран с 2 кнопками (стартовый). Как же нам проверить, что в коде все сделано на совесть и после смерти процесса все будет сохранено? Берем эмулятор, в нем ставим галку на Не хранить активити и пишем наш тест.

AddNewPairRestoreAfterDeathTest — Создаем новый элемент, вбиваем какие-то данные, после чего жмем на кнопку Home тем самым сворачивая приложение, после чего открываем его вновь. Проверяем что все введенные данные были сохранены, жмем сохранить и проверяем, что в списке категорий добавился новый элемент.

Здесь нас интересует метод goHomeAndReturnToApp(), который лежит в классе BaseTest. Суть в том, что у Espresso ограниченный функционал и для таких вещей как свернуть приложение и восстановить из списка недавних у нас есть такой замечательный инструмент как UI-automator (androidTestImplementation ‘androidx.test.uiautomator:uiautomator:2.2.0’)

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

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

Запись опубликована в рубрике Программирование Android. Добавьте в закладки постоянную ссылку.