Ålonelybytes— one more developer

Автотестирование в iOS. Юнит-тесты и UI Automation

На автотестирование всегда чего-то не хватает. Жалко времени, жалко денег. Действительно, часто соотношение качества работы и затраченного времени — достаточно, чтобы автотесты не делать (полагаясь на традиционное тестирование). Ангстрем жил без тестов почти год, но после того, как в очередном апдейте «внезапно» пропала русская клавиатура, я сдался и организовал несколько тысяч тестов, покрывающих почти всю основную функциональность приложения.

Что такое автотест?

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

Потыкать в разные части экрана всеми пальцами сразу и посмотреть, чтобы ничего не сломалось.

А вот четкую последовательность действий можно оформить это в виде небольшой программы:

Нажать на клавиатуре кнопки «1», «м». Проверить, что справа получилось «3,28 ft».

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

Типы тестов

Для Ангстрема пришлось использовать несколько разных типов тестов:

Тесты элементарной функциональности (юнит-тесты)

Юнит-тесты работают с кодом. В идеале берется каждый метод, и пишется тест, подающий разные значения параметров и проверющий корректность результата. К примеру, сравнительно легко так протестировать функцию умножения. И, наоборот, проверить метод, запускающий анимацию вьюшки — трудно. При разработке iOS-приложений часто получается, что 80% приложения — это UI, на который нет чёткого ТЗ и на создание которого очень мало времени. В таких условиях тесты писать тяжело.

Правила и сложности при разработке юнит-тестов много где описаны. Это правильный, «тестируемый», код, использование стандартных библиотек и многое другое. Всё это практически ничем не отличается от других языков.

Юнит-тестами я проверяю:

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

К каждому тесту я создал специальный файл с проверками, чтобы можно было легко их добавлять и исправлять. Вот, например, кусок JSON-файла с тестами диктовки и парсинга буфера обмена (слева — текст, который проверяется, справа — корректный результат):

“$200”: “200 $ (USA Dollar)”

“200$”: “200 $ (USA Dollar)”

“семь градусов Цельсия”: “7 °C (degree Celsius)”

UI-тесты

В 2010 году Эпл показала новое средство тестирования приложений, которое называется UI Automation. К сожалению, тогда «виртуальные тыки» не работали с локализованным интерфейсом и плохо работали с его динамическими изменениями, поэтому использование пришлось отложить. Сейчас проблем с локализацией нет.

Я попробовал UI Automation для:

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

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

Тесты получились почти все. Некоторые, также, как и видео, требуют сложной работы с интерфейсом, поэтому пока не работают.

Чтобы их проще было писать и запускать (пишутся они на JavaScript’е, запускаются из Терминала), я подготовил:

Вместе с багами в самом UI Automation, было несколько больших проблем.

Работа с буфером обмена. Чтобы её сымитировать, пришлось завести ненастоящий и почти невидимый (совсем невидимый не редактируется) UITextField и вставить туда то, что скопировано, после чего проверить вставленное. Кстати, я узнал, что минимально возможный размер UITextField, в который что-то можно ввести — 2х2 поинта. Почему так, интересно?

Нажатие на конкретное место в большом контроле. Это пришлось реализовать для цифровой клавиатуры. Она сделана не несколькими кнопками, а одним большим контролом, который обрабатывает нажатия (так можно точнее контролировать внешний вид). Впрочем, для коротких тапов все сравнительно просто. А вот лонг-тап сейчас (благодаря целой группе недоработок в UI Automation) делается только в центре контрола. Так что минус (который получается долгим тапом на точку на цифровой клавиатуре) у меня так и не протестирован.

Пожалуй, самая большая проблема заключается в том, как проверить правильность результата тыков и свайпов. С экранных контролов считывать данные плохо, они зависят от языка. Мне же нужно проверить отдельно то, как отображаются единицы (это частично проверяется юнит-тестами и будет проверять человек), и то, как происходит преобразование. Выкрутился я, опять же, фейковым UILable’ом. Поставив несколько таких лейблов (невидимых, за пределами экрана), я проставляю в них специальные тестовые строки, которые точно отражают то, что я вижу на экране. Считать строку из лейбла не составляет труда, после чего можно сравнить её с тестовой.

В результате получилось протестировать почти всё:

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

Accessibility

Решение, которое позволило при помощи UI Automation работать с разными языками, называется Accessibility. Изначально оно предназначено для улучшения работы с интерфейсом людей с ограниченными возможностями, но с пятой версии iOS появилась возможность каждой вьюшке назначить accessibilityIdentifier, который позволяет в процессе тестирования найти нужный компонент как-то так:

var keypad = UIATarget.localTarget().frontMostApp().elements()[“Keypad”];

Получается, что если у вас в приложении хорошо поддержана Accessibility («любое приложение должно его поддерживать!», — сказал перфекционист во мне и вздохнул), то и тестировать будет несложно. И наоборот, если подготовить приложение для тестов, то до нормальной поддержки Accessibility останется один шаг.

Как написать свои тесты?

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

Дождитесь, когда устаканится та функциональность, которую хочется оттестировать. Пишете тесты на API, дождитесь, пока он появится на сервере. Впрочем, на API часто есть более чёткое ТЗ, и тесты можно писать заранее, про это смотрите TDD. Проведите традиционное тестирование перед тем, как писать автотесты. 3.

Сфорулируйте правила, по которым должна работать функциональность и метрики, которые нужно проверять. Это, скорее всего, поможет создать специальные методы для проверки, которые помогут в разработке тестов. 4.

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

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

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

Что дальше?

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

А у вас какой опыт в тестировании приложений для iPhone и iPad?

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