Здравсвтуйте, друзья,
Пост посвящен тому, как оттестировать множественные переходы между разными приложениями с выполнением некоторых действий внутри приложений.
Казалось бы тривиальная задача :) На самом деле, на сегодняшний день существует только одно ее простое решение (и одно сложное через MonkeyRunner). Именно этим решением я и хочу с вами поделиться :)
Для начала рассмотрим, какие подходы в целом существуют для Android UI тестирования, а так же их за и против:
1. Jayway Robotium Solo (http://code.google.com/p/robotium/) - open source библиотека для black box тестирования.
За: идеальное black box тестирование с огромными возможностями assert-ов и взаимодействия с приложением.
Против: невозможно протестировать user-like переход между приложениями, т.к. библиотека не может выйти за рамки target package - ограничено тестируемым приложением.
2. Monkey (http://developer.android.com/guide/developing/tools/monkey.html). Обезъяныч просто посылает большое число различных команд приложению (тачи, свайпы и так далее)
За: можно нагрузить приложение, для проверки его стабильности, в случае, когда нужно протестировать monkey-like взаимодействие с приложением
Против: невозможно создавать сценарии или воспроизводить баги по результатам работы обезьянки. Ограничено тестируемым приложением.
3. Android MonkeyRunner (http://developer.android.com/guide/developing/tools/monkeyrunner_concepts.html). Скажу только, что это очень серьезная и крутая тулза, которая не подходит под заголовок "Простая автоматизация". В целом, MonkeyRunner является продолжением данного поста и использует Python, что может оказаться подводным камнем для многих. Опишу в будущем отдельно это утилиту.
4. Использовать shell скрипт, на подобие того, как было описано в посте: http://dev.by/blog/54818.
За: полная свобода передвижения по девайсу/эмулятору и полная свобода взаимодействия с устройством. Можно создавать достаточно сложные сценарии.
Против: тяжело обстоит ситуация с assert-aми. Я смог найти только возможность делать assert для проверки, какая активити в фокусе, заблокировано ли устройство, открыта ли экранная клавиатура и так далее (проверка того, что можно получить из adb shell dumpsys). Требует определенной усидчивости и настойчивости.
5. Для справки: Web UI тестирование для Android можно осуществить с помощью Android Selenium driver: http://code.google.com/p/selenium/wiki/AndroidDriver
Нельзя сказать, что какой-то из этих подходов супер, а другой полная фигня. Все они нацелены на свои задачи, как инструменты: пила, молоток, кувалда и напильник :)
Рассмотрим более подробно подход через shell скрипты (так, как это "простая автоматизация", то мы исключим assert-ы фокусированных активити), для этого нам нужно освоится с командами:
- adb shell getevent (Дампит происходящие события физического уровня на девайсе)
- adb shell sendevent (Позволяет воспроизводить события физического уровня на девайсе)
- adb shell dumpsys window (Дампит текущую информацию сервиса отрисовки дисплея, содержит имя текущей активити в фокусе)
- sleep n, где n - интервал ожидания от 0 до дофига в секундах, причем если нужно подождать в милисекундах, то необходимо использовать значения с плавающей точкой: sleep 0.1 - подождет 100 милисекунд.
add device 1: /dev/input/event0
name: "qwerty2"
could not get driver version for /dev/input/mouse0, Not a typewriter
could not get driver version for /dev/input/mice, Not a typewriter
Делаем быстрый клик мышью на иконке меню, чтобы открыть список приложений:
/dev/input/event0: 0003 0000 0000010f
/dev/input/event0: 0003 0001 000001cf
/dev/input/event0: 0001 014a 00000001
/dev/input/event0: 0000 0000 00000000
/dev/input/event0: 0001 014a 00000000
/dev/input/event0: 0000 0000 00000000
Поздравляю, мы получили дамп простого тача дисплея :)
Теперь если мы переведем все числа в десятичную систему:
/dev/input/event0: 3 0 250 # x
/dev/input/event0: 3 1 770 # y
/dev/input/event0: 1 330 1 # touch down
/dev/input/event0: 0 0 0 # event separator
/dev/input/event0: 1 330 0 # touch up
/dev/input/event0: 0 0 0 # click is finished
Теперь добавим вначале каждой строки adb shell sendevent и уберем все двоеточия:
adb shell sendevent /dev/input/event0 3 0 250 # x
adb shell sendevent /dev/input/event0 3 1 770 # y
adb shell sendevent /dev/input/event0 1 330 1 # touch down
adb shell sendevent /dev/input/event0 0 0 0 # event separator
adb shell sendevent /dev/input/event0 1 330 0 # touch up
adb shell sendevent /dev/input/event0 0 0 0 # click is finished
Записываем данную последовательность в файл (click.sh)и выполняем (не забудте сделать chmod 777 click.sh, чтобы разрешить выполнение).
В моем случае, я одной и той же командой могу заходить в меню приложений и выходить из него. Теперь параметризируем этот файл:
adb shell sendevent /dev/input/event0 3 0 $1 # x
adb shell sendevent /dev/input/event0 3 1 $2 # y
adb shell sendevent /dev/input/event0 1 330 1 # touch down
adb shell sendevent /dev/input/event0 0 0 0 # event separator
adb shell sendevent /dev/input/event0 1 330 0 # touch up
adb shell sendevent /dev/input/event0 0 0 0 # click is finished
Вызов: ~$ ./click.sh 250 770
Таким способом мы можем написать параметризированный скрипт для воспроизведения клика по экрану для любого девайса/эмулятора.
На заметку: если вам необходимо воспроизвести swipe (например, чтобы разблокировать девайс). То сделать это проще простого, нужно отправить устройству много touch down событий с изменяющимеся координатами x или y, в зависимости от swipe, разделим click.sh на click_down.sh и click_up.sh:
click_down.sh:
adb shell sendevent /dev/input/event0 3 0 $1 # x
adb shell sendevent /dev/input/event0 3 1 $2 # y
adb shell sendevent /dev/input/event0 1 330 1 # touch down
adb shell sendevent /dev/input/event0 0 0 0 # event separator
click_up.sh:
adb shell sendevent /dev/input/event0 1 330 0 # touch up
adb shell sendevent /dev/input/event0 0 0 0 # click is finished
На моем эмуляторе точка, с которой нужно начинать разблокировку экрана: 83:608.
Создадим еще один строительный блок для наших шелл тестов - unlock_screen.sh:
./click_down.sh 83 608
./click_down.sh 150 608
./click_down.sh 250 608
./click_down.sh 350 608
./click_down.sh 450 608
./click_up.sh
Теперь разберемся, как нажимать кнопку Power и Back:
1. Записываем последовательность событий с помощью adb shell getevent:
Power:
/dev/input/event0: 0001 0074 00000001
/dev/input/event0: 0001 0074 00000000
Back:
/dev/input/event0: 0001 009e 00000001
/dev/input/event0: 0001 009e 00000000
2. Декодируем:
Power:
adb shell sendevent /dev/input/event0 1 116 1 # Pressed
adb shell sendevent /dev/input/event0 1 116 0 # Realeased
Back:
adb shell sendevent /dev/input/event0 1 158 1 # Pressed
adb shell sendevent /dev/input/event0 1 158 0 # Realeased
Аналогично можно записать команду для любой кнопки эмулятора. Отличие от физического устройства будет в том,
что на физическом устройстве будет другой интерфейс (у эмулятора все на одном интерфейсе), например, тач скрин
event2, а кнопки на event1.
Сохраняем наш строительный кирпичик в файл click_power.sh и click_back.sh соответственно (не забываем делать chmod 777).
Теперь у нас есть следующие блоки будущего тестового сценария:
- click_down.sh x y
- click_up.sh
- click.sh x y
- unlock_screen.sh
- click_power.sh
- click_back.sh
- Нажимаем кнопку power, чтобы включить экран устройства
- Разблокируем экран
- Заходим в меню приложений
- Открываем по очереди первых пять приложений (выходим из приложений кнопкой Back, палагаем, что приложение безусловно выйдет)
- Блокируем экран кнопкой power
./click_power.sh
./unlock_screen.sh
sleep 2
./click.sh 250 770 # из примера выше, на Андроид 2.2 эмуляторе попадает как раз в иконку с приложениями
sleep 2
./click.sh 67 123 # кликаем первое приложение
sleep 5
./click_back.sh
sleep 1
./click.sh 174 123 # кликаем второе приложение
sleep 5
./click_back.sh
sleep 1
./click.sh 422 123 # кликаем третье приложение
sleep 5
./click_back.sh
sleep 1
./click.sh 250 770 # из примера выше, на Андроид 2.2 эмуляторе попадает как раз в иконку с приложениями
sleep 2
./click_power.sh
Оптимизация: в конец каждого скрипта добавить параметр sleep, чтобы не писать везде sleep X. Тогда сценарий будет выглядеть совсем чудно:
./click_power.sh 1
./unlock_screen.sh 2
./click.sh 250 770 2
./click.sh 67 123 5
./click_back.sh 1
./click.sh 174 123 5
./click_back.sh 1
./click.sh 422 123 5
./click_back.sh 1
./click.sh 250 770 2
./click_power.sh
Путем подобных манипуляций можно строить достаточно сложные стресс тесты. Хочу заметить, что в промышленных масштабах, данный подход
выявил большее число багов (на протяжении 2х лет), чем любой другой :)
Что примечательно, данный подход очень легко поддерживать, в отличие от тех, которые зависят от исходного кода продукта.
Чтобы добавить assert-ы на активити, которая в фокусе, необходимо изучить вывод
~$ adb shell dumpsys window
Дамп данного сервиса, даст нам важную строку:
mCurrentFocus=Window{45062ac0 com.android.launcher/com.android.launcher2.Launcher paused=false}
Сообщит нам о том, какая сейчас активити в фокусе :)
Чтобы получать только нужную нам строку, можем добавить grep:
~$ adb shell dumpsys window | grep mCurrentFocus
Для получения состояния дисплея, необходимо изучить следующие строки:
KeyWaiter state:
mLastWin=null mLastBinder=null
mFinished=true mGotFirstWindow=true mEventDispatching=true mTimeToSwitch=0
mEventDispatching=true - экран не заблокирован.
mEventDispatching=false - экран заблокирован.
На заметку: данный шелл сценарий, если убрать везде adb shell можно поместить на девайс и вызвать программно из Java кода :D
Я думаю не стоит даже рассказывать, какие гаризонты сразу покоряются ^_^
Желаю вам успехов в повышение качества ваших продуктов, но теперь с меньшим количеством ежедневной рутины (:
С уважением,
Егор
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.