Я хочу рассказать вам о замечательном инструменте для автоматического тестирования пользовательского интерфейса в приложениях Android - UiAutomator. Вы можете найти всю актуальную документацию здесь: http://developer.android.com/tools/help/uiautomator/index.htmlи http://developer.android.com/tools/testing/testing_ui.html
У UiAutomator есть свои плюсы и минусы.
Преимущества:
- Он может быть использован на дисплеях устройств с различным разрешением.
- События можно привязывать к элементам управления Android UI. К примеру, кликайте на кнопку с текстом «Ок» вместо того, чтобы кликать по расположению координат (x=450, y=550).
- Инструмент может воспроизводить сложную последовательность действий пользователя.
- Он всегда выполняет одну и ту же последовательность действий, позволяя нам собирать данные о производительности на разных устройствах.
- Он может многократно выполняться на различных устройствах без изменения Java-кода.
- Может использовать аппаратные кнопки на устройствах.
Недостатки:
- Его сложно использовать с OpenGL- и HTML5-приложениями, поскольку они не имеют компонентов Android UI.
- Время, затрачиваемое на написание JavaScript.
Разработка скрипта
Чтобы познакомить вас с работой UiAutomator, я хочу продемонстрировать простую программу. Это стандартное Android-приложение для месседжинга, которое может отсылать SMS-сообщения на любой телефонный номер.
Вот короткое описание действий, которые мы осуществили:
- Найти и запустить приложение
- Создать и отправить SMS-сообщения
Как видите, это очень просто.
Подготовка к тесту
Для анализа интерфейса будем использовать uiautomatorviewer.
Uiautomatorviewer показывает разделенный скриншот всех компонентов UI в Node Detail, чтобы вы могли видеть их разнообразные свойства. Там же в свойствах вы можете найти искомый элемент.
Настройка среды разработки
Если вы используете Eclipse*:
- Создайте новый Java-проект в Eclipse. Мы назовем наш проект: SendMessage.
- Клик правой кнопкой на вашем проекте в ProjectExplorerи затем клик на Properties.
- В Propertiesвыберите JavaBuildPath и добавьте требуемые библиотеки:
Если вы пользуетесь иной средой разработки, убедитесь что файлы android.jar и uiautomator.jar добавлены в настройки проекта.
Кликните AddLibrary > JUnit и там выберите JUnit3, чтобы добавить поддержку для JUnitКликните Add External JARs ...В <android-sdk>/platforms/directory выберите последнюю версию SDK. Также в этой директории выберите файлы uiautomator.jar и android.jar
Создание скрипта
Создайте проект в ранее созданном новом файле с классом Java. Назовите его SendMessage. Этот класс унаследован из класса UiAutomatorTestCase. Используя Ctrl + Shift + o (для Eclipse), добавьте требуемые библиотеки.
Создайте три функции для тестирования этого приложения:
- Найти и запустить приложение
- Отправить SMS-сообщения
- Выйти в главное меню приложения
Создайте функцию, от которой будут запускаться все эти опции, – своего рода основную функцию:
public void test() { // Here will be called for all other functions }
Функция поиска и запуска приложения
Эта функция крайне проста. Мы нажимаем кнопку Домой, открываем меню и ищем значок приложения. Нажимаем и запускаем его.
private void findAndRunApp() throws UiObjectNotFoundException { // Go to main screen getUiDevice().pressHome(); // Find menu button UiObject allAppsButton = new UiObject(new UiSelector() .description("Apps")); // Click on menu button and wait new window allAppsButton.clickAndWaitForNewWindow(); // Find App tab UiObject appsTab = new UiObject(new UiSelector() .text("Apps")); // Click on app tab appsTab.click(); // Find scroll object (menu scroll) UiScrollable appViews = new UiScrollable(new UiSelector() .scrollable(true)); // Set the swiping mode to horizontal (the default is vertical) appViews.setAsHorizontalList(); // Find Messaging application UiObject settingsApp = appViews.getChildByText(new UiSelector() .className("android.widget.TextView"), "Messaging"); // Open Messaging application settingsApp.clickAndWaitForNewWindow(); // Validate that the package name is the expected one UiObject settingsValidation = new UiObject(new UiSelector() .packageName("com.android.mms")); assertTrue("Unable to detect Messaging", settingsValidation.exists()); }
Все наименования классов, текст на кнопках и пр. - из uiautomatorviewer.
Посылка SMS-сообщения
Эта функция находит и нажимает кнопку создания нового сообщения, вводит телефонный номер адресата и нажимает кнопку отправки. Телефонный номер и текст проходят через параметры функции:
private void sendMessage(String toNumber, String text) throws UiObjectNotFoundException { // Find and click New message button UiObject newMessageButton = new UiObject(new UiSelector() .className("android.widget.TextView").description("New message")); newMessageButton.clickAndWaitForNewWindow(); // Find to box and enter the number into it UiObject toBox = new UiObject(new UiSelector() .className("android.widget.MultiAutoCompleteTextView").instance(0)); toBox.setText(toNumber); // Find text box and enter the message into it UiObject textBox = new UiObject(new UiSelector() .className("android.widget.EditText").instance(0)); textBox.setText(text); // Find send button and send message UiObject sendButton = new UiObject(new UiSelector() .className("android.widget.ImageButton").description("Send")); sendButton.click(); }
Отображаемые поля для телефонного номера и текстового сообщения не имеют никаких специальных свойств: ни текста, ни какого-то иного описания для этих полей не предусмотрено. Поэтому в данном случае я могу найти их, используя элемент под его порядковым номером в иерархии интерфейса.
Чтобы добавить возможность передавать параметры в скрипт, мы можем точно определить число адресатов и сами сообщения. Функция test() устанавливает параметры по умолчанию, и если какими-либо из параметров были отправленные через командную строку сообщения, заменой параметрам по умолчанию были бы:
// Default parameters String toNumber = "123456"; String text = "Test message"; String toParam = getParams().getString("to"); String textParam = getParams().getString("text"); if (toParam != null) { // Remove spaces toNumber = toParam.trim(); } if (textParam != null) { text = textParam.trim(); }
Таким образом, мы сможем передавать параметры из командной строки скрипта, используя ключ –e, имя параметра и значение. К примеру, мое приложение отсылает номер для отправки " 777777 »:-e to 777777
Есть и некоторые подвохи. К примеру, данное приложение не понимает некоторые символы, и происходит сбой. Вот некоторые из этих символов: space, &, <, > , (,) , ", ' , а также некоторые символы Unicode. При внедрении в скрипт я заменил эти символы текстовой строкой, например пробел : blogspaceblog. Получается, когда скрипт запускает UiAutomator, мы используем скрипт, который будет обрабатывать наши входные параметры. Мы добавляем в функцию test(), проверку наличия опций, парсим параметры и заменяем их реальными символами. Ниже представлен образец кода, наглядно демонстрирующий всё, что мы ввели ранее:
if (toParam != null) { toParam = toParam.replace("blogspaceblog", ""); toParam = toParam.replace("blogamperblog", "&"); toParam = toParam.replace("bloglessblog", "<"); toParam = toParam.replace("blogmoreblog", ">"); toParam = toParam.replace("blogopenbktblog", "("); toParam = toParam.replace("blogclosebktblog", ")"); toParam = toParam.replace("blogonequoteblog", "'"); toParam = toParam.replace("blogtwicequoteblog", """); toNumber = toParam.trim(); } if (textParam != null) { textParam = textParam.replace("blogspaceblog", ""); textParam = textParam.replace("blogamperblog", "&"); textParam = textParam.replace("bloglessblog", "<"); textParam = textParam.replace("blogmoreblog", ">"); textParam = textParam.replace("blogopenbktblog", "("); textParam = textParam.replace("blogclosebktblog", ")"); textParam = textParam.replace("blogonequoteblog", "'"); textParam = textParam.replace("blogtwicequoteblog", """); text = textParam.trim(); }
Выход в главное меню приложения
Эта функция является самой простой из тех, что мы применили. Я просто нажимаю кнопку возврата цикла до момента, пока не покажется кнопка создания нового сообщения.
private void exitToMainWindow() { // Find New message button UiObject newMessageButton = new UiObject(new UiSelector() .className("android.widget.TextView").description("New message")); // Press back button while new message button doesn't exist while(!newMessageButton.exists()) { getUiDevice().pressBack(); } }
Исходный код
Вот и наш код:
package blog.send.message; import com.android.uiautomator.core.UiObject; import com.android.uiautomator.core.UiObjectNotFoundException; import com.android.uiautomator.core.UiScrollable; import com.android.uiautomator.core.UiSelector; import com.android.uiautomator.testrunner.UiAutomatorTestCase; public class SendMessage extends UiAutomatorTestCase { public void test() throws UiObjectNotFoundException { // Default parameters String toNumber = "123456"; String text = "Test message"; String toParam = getParams().getString("to"); String textParam = getParams().getString("text"); if (toParam != null) { toParam = toParam.replace("blogspaceblog", ""); toParam = toParam.replace("blogamperblog", "&"); toParam = toParam.replace("bloglessblog", "<"); toParam = toParam.replace("blogmoreblog", ">"); toParam = toParam.replace("blogopenbktblog", "("); toParam = toParam.replace("blogclosebktblog", ")"); toParam = toParam.replace("blogonequoteblog", "'"); toParam = toParam.replace("blogtwicequoteblog", """); toNumber = toParam.trim(); } if (textParam != null) { textParam = textParam.replace("blogspaceblog", ""); textParam = textParam.replace("blogamperblog", "&"); textParam = textParam.replace("bloglessblog", "<"); textParam = textParam.replace("blogmoreblog", ">"); textParam = textParam.replace("blogopenbktblog", "("); textParam = textParam.replace("blogclosebktblog", ")"); textParam = textParam.replace("blogonequoteblog", "'"); textParam = textParam.replace("blogtwicequoteblog", """); text = textParam.trim(); } findAndRunApp(); sendMessage(toNumber, text); exitToMainWindow(); } // Here will be called for all other functions private void findAndRunApp() throws UiObjectNotFoundException { // Go to main screen getUiDevice().pressHome(); // Find menu button UiObject allAppsButton = new UiObject(new UiSelector() .description("Apps")); // Click on menu button and wait new window allAppsButton.clickAndWaitForNewWindow(); // Find App tab UiObject appsTab = new UiObject(new UiSelector() .text("Apps")); // Click on app tab appsTab.click(); // Find scroll object (menu scroll) UiScrollable appViews = new UiScrollable(new UiSelector() .scrollable(true)); // Set the swiping mode to horizontal (the default is vertical) appViews.setAsHorizontalList(); // Find Messaging application UiObject settingsApp = appViews.getChildByText(new UiSelector() .className("android.widget.TextView"), "Messaging"); // Open Messaging application settingsApp.clickAndWaitForNewWindow(); // Validate that the package name is the expected one UiObject settingsValidation = new UiObject(new UiSelector() .packageName("com.android.mms")); assertTrue("Unable to detect Messaging", settingsValidation.exists()); } private void sendMessage(String toNumber, String text) throws UiObjectNotFoundException { // Find and click New message button UiObject newMessageButton = new UiObject(new UiSelector() .className("android.widget.TextView").description("New message")); newMessageButton.clickAndWaitForNewWindow(); // Find to box and enter the number into it UiObject toBox = new UiObject(new UiSelector() .className("android.widget.MultiAutoCompleteTextView").instance(0)); toBox.setText(toNumber); // Find text box and enter the message into it UiObject textBox = new UiObject(new UiSelector() .className("android.widget.EditText").instance(0)); textBox.setText(text); // Find send button and send message UiObject sendButton = new UiObject(new UiSelector() .className("android.widget.ImageButton").description("Send")); sendButton.click(); } private void exitToMainWindow() { // Find New message button UiObject newMessageButton = new UiObject(new UiSelector() .className("android.widget.TextView").description("New message")); // Press back button while new message button doesn't exist while(!newMessageButton.exists()) { getUiDevice().pressBack(); sleep(500); } } }
Компиляция и выполнение тестирования UiAutomator
- Чтобы сгенерировать файлы конфигурации для экспериментальной сборки, выполните следующую команду из командной строки:
<android-sdk>/tools/androidсоздать uitest-проект -n<name> -t <target-id> -p<path>
где <name>- это имя проекта, который был создан для тестирования UiAutomator (в нашем случае: SendMessage), <target-id>- выбор устройства и AndroidAPILevel (вы можете получить перечень установленных устройств с помощью команды <android-sdk> / tools / androidlisttargets), и <path> это путь к директории, где содержится проект. - Вы должны экспортировать переменную среды ANDROID_HOME:
- На Windows:
установите ANDROID_HOME=<путь_к_вашему_sdk> - На UNIX:
экспортируйте ANDROID_HOME=<путь_к_вашему_sdk>
- На Windows:
- Откройте директорию с файлом проекта build.xml, который был создан на этапе 1, и выполните команду: antbuild
- Скопируйте скомпилированный файл JAR на устройство, используя adb push:
adbpush<путь_к_output_jar> /data/local/tmp/
В нашем случае это:
adbpush<project_dir>/bin/SendMessage.jar /data/local/tmp/ - И выполните скрипт:
adb shell uiautomator runtest /data/local/tmp/SendMessage.jar –c blog.send.message.SendMessage
Об авторе
Егор Чураев (egor.churaev@intel.com) – Стажер по программному обеспечению
Дополнительные материалы: