Компания
166,75
рейтинг
17 декабря 2013 в 13:29

Разработка → Автоматизация тестирования Android приложений с помощью UIAutomator


Источник фото
Тестирование — это очень важный процесс во время разработки приложений. В случае Android, тестирование приложения следует производить на большом количестве устройств, в связи с тем, что многие из них имеют значительные различия по характеристикам (разрешение экрана, версия Android и т.д.). Процесс тестирования приложения вручную на большом количестве устройств может быть трудоемок, утомителен и подвержен ошибкам. Более эффективный и надежный подход состоит в автоматизации тестирования пользовательского интерфейса. С помощью UIAutomator можно разработать тест-скрипт, который будет работать на множестве Android устройств
с одинаковой точностью и воспроизводимостью.

UIAutomator


UIAutomator разрабатывается корпорацией Google и поставляется вместе с Android SDK. UIAutomator – это аналог инструмента UIAutomation компании Apple для тестирования Android приложений. Android SDK предоставляет следующие инструменты для поддержки автоматизированного функционального тестирования пользовательского интерфейса:

  • UIAutomatorviewer — графический инструмент для распознавания компонентов пользовательского интерфейса в Android приложении;
  • UIAutomator — библиотеки Java API, содержащие методы для создания тестов пользовательского интерфейса.

Чтобы использовать эти инструменты, необходимо установить следующие компоненты среды Android:

  • Android SDK Tools, версия 21 или выше;
  • Платформа Android SDK, c API 16 или выше.

UIAutomator постоянно дорабатывается, поэтому самую свежую документацию можно найти на сайте: http://developer.android.com/tools/help/uiautomator/index.html.

Преимущества UIAutomator, для тестирования приложений:

  • Отсутствие зависимости от разрешения экрана;
  • Действия привязываются к Android UI компонентам. Это позволяет работать напрямую с элементами пользовательского интерфейса. Например, если необходимо нажать кнопку «ОК», можно средствами UIAutomator API отправить скрипту команду: нажми кнопку с надписью «ОК», и он её нажимает. Таким образом не приходится привязываться к координатам;
  • Можно воспроизводить сложные последовательности действий пользователя, и всегда эта последовательность будет одинаковой;
  • Тесты могут быть запущены необходимое количество раз на различных устройствах без необходимости изменения Java кода;
  • Можно использовать внешние кнопки на устройстве (кнопка «назад», «выключить», «громкость» и т.д.).

Но есть и недостатки:

  • Тяжело использовать для приложений, написанных на HTML 5 и OpenGL, так как в этих приложениях нет Android UI элементов. В связи с этим, необходимо либо привязываться к координатам, либо искать альтернативные варианты тестирования;
  • Необходимо проверять, и в случае необходимости, обновлять Java скрипты при обновлении Android приложения.

Тестирование приложения с помощью UIAutomator состоит из следующих шагов:

  1. Подготовка к тесту: установка приложения на устройство, анализ его UI компонент;
  2. Создание автоматизированного теста для приложения;
  3. Компиляция теста в JAR файл и копирование его на устройство;
  4. Запуск теста и анализ результатов;
  5. Исправление различных ошибок, найденых в процессе тестирования.

Разработка скрипта


Для ознакомления с технологиями UIAutomator далее будет представлена простая програма, осуществляющая несложные действия с устройством. В качестве тестируемого приложения будет использовано стандартное Android приложение — Messaging, а UIAutomator будет отправлять SMS-сообщение на определенный номер.

Определим действия, которые будут реализованы в тесте:

  1. Поиск и запуск приложения;
  2. Создание и отправка сообщения.

Как видите, все достаточно просто.

Подготовка к тесту


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

image

Для анализа пользовательского интерфейса необходимо:

  • Присоединить Android устройство к компьютеру;
  • Запустить UIAutomatorviewer, находящийся в: <android-sdk>/tools/
    $ uiautomatorviewer
    
  • Нажать на кнопку Device Screenshot в UIAutomatorviewer для захвата изображения с устройства;
  • При выделении определенного элемента интерфейса на панели справа отображаются его свойства. По этим свойствам элемент легко может быть найден в тест-скрипте.

Если UIAutomatorviewer не может разложить изображение на компоненты, значит, приложение написано с использованием HTML 5 или OpenGL.

Настройка среды разработки


Если вы используете Eclipse:

  • Создаем новый Java проект в Eclipse. В этом проекте будем реализовывать UIAutomator скрипт. Назовем проект: SendMessage
  • Вызываем контекстное меню нажав правой кнопкой мыши на проект в Project Explorer, и выбираем пункт Properties. В Properties находим Java Build Path и добавляем необходимые библиотеки:
    1. Для добавления поддержки JUnit необходимо выбрать Add Library > JUnit;
    2. Нажав Add External JARs… необходимо перейти в директорию с последней версией SDK в <android-sdk>/platforms/ и выбрать в ней 2 файла: uiautomator.jar и android.jar

При использовании другой среды разработки, убедитесь, что файлы UIAutomator.jar и android.jar добавлены в настройки проекта.

UIAutomator API


Чтобы рассказать обо всех возможностях, которыми обладает UIAutomator для создания тест-скриптов, потребуется достаточно большое количество времени. Всю подробную информцию можно найти на сайте: http://developer.android.com/tools/help/UIAutomator/index.html

Создание скрипта


Для начала необходимо создать в проекте «SendMessage» новый файл с Java классом, например под именем SendMessage. Этот класс должен быть унаследован от класса UIAutomatorTestCase. Чтобы добавить библиотеку в Eclipse, достаточно воспользоваться сочетанием клавиш Ctrl+Shift+o. Аналогичным образом добавляются и другие библиотеки, поэтому не буду больше заострять на этом внимание.

Создадим три функции для тестирования основного функционала приложения:

  1. Поиск и запуск приложения
  2. Отправка SMS сообщения
  3. Выход в главное меню приложения

Первая функция, запускающая все эти методы, своего рода main функция, будет выглядеть следующим образом:

public void test() {
	    // Here will be called for all other functions
	}

Функция для поиска и запуска приложения


Функция будет осуществлять следующие действия: нажать на кнопку Home, для перехода в главном окно, открыть меню и найти значок с нужным приложением, запустить это приложение.
Поиск и запуск приложения
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 сообщения


В этой функции реализуем поиск и нажатие на кнопку создания нового сообщения, ввод номера телефона, текста сообщения и его последующая отправка. Номер и текст будут передаваться через аргументы функции:
Функция отправки 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();
	}


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

Реализуем возможность получения в качестве параметров номера телефона получателя, а также текст сообщения. В функцию 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. После него передается 2 значения: имя параметра и значение. Например, для передачи номера «777777», в качестве номера получателя, передадим параметры: -e to 777777. Для получения этих параметров в скрипте используется метод: getParams().

Но тут есть и несколько подводных камней. Например, не получается передать текст с некоторыми символами, UIAutomator их не воспринимает (пробел, &, <, >, (, ), “, ’, и т.д., а так же юникодные символы). Для этого предлагаю делать замену этих символов при подаче их в скрипт какой-нибудь строкой, например, пробел заменим строкой: blogspaceblog. Это удобно, когда для запуска скрипта UIAutomator используем скрипт, который будет обрабатывать входные параметры. Добавим в проверку входных параметров парсинг и замену этих строк:
Код замены строк
        if (toParam != null) {
			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();
		}
	}

Сбор логов с теста


Для того, чтобы логировать результаты теста, можно использовать стандартный буффер Android. Для работы с ним необходимо подключить библиотеку в скрипте:
import android.util.Log;

Всю информацию, которая интересна можно записывать в логи. Это можно делать с помощью функции:
Log.i(String title, String title);

Логи можно читать с устройства с помощью команды:
$ adb logcat

Более подробную информацию по logcat можно найти на официальном сайте: developer.android.com/tools/help/logcat.html

Получившийся код


Таким образом, у нас получился такой код:
Посмотреть код
package blog.send.message;
import android.util.Log;
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) {
			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();
		}
                Log.i("SendMessageTest", "Start SendMessage");
		findAndRunApp();
	    	sendMessage(toNumber, text);
	    	exitToMainWindow();
                Log.i("SendMessageTest", "End SendMessage");
	}
	// 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 теста


  1. Для генерации файлов конфигурации сборки теста необходимо выполнить следующую команду в терминале:
    $ <android-sdk>/tools/android create uitest-project -n <name> -t  <target-id> -p <path>
    Где - имя проекта, который создавался для теста UIAutomator (в нашем случае: SendMessage), <target-id> - выбор устройства и Android API Level (можно получить список установленных устройств командой: <android-sdk>/tools/android list targets) и - путь к директории с проектом.
    Необходимо экспортировать переменную окружения ANDROID_HOME:
    • Windows:
      set ANDROID_HOME=<path_to_your_sdk>
    • Unix:
      export ANDROID_HOME=<path_to_your_sdk>

    Заходим в директорию с проектом, в которой лежит сгенерированный на шаге 1 файл build.xml и выполняем команду:
    $ ant build

    Копируем собранный JAR файл на устройство с помощью команды adb push:
    $ adb push <path_to_output_jar> /data/local/tmp/
    Для нашего случая:
    $ adb push <project_dir>/bin/SendMessage.jar /data/local/tmp/

    Запускаем скрипт:
    $ adb shell uiautomator runtest /data/local/tmp/SendMessage.jar –c blog.send.message.SendMessage -e to 777777


    Заключение


    Использование UIAutomator очень удобно для качественного тестирования приложений на большом количестве девайсов. С помощью этой технологии можно создавать различные Demo для демонстрации приложения.
Автор: @echuraev
Intel
рейтинг 166,75

Комментарии (13)

  • 0
    чем это лучше роботиума?
    • 0
      С роботиумом я почти не работал. Я слышал, что в роботиуме есть баги и тесты выполняются медленно, лично не сталкивался с этим, поэтому информация может быть уже устаревшей. В процессе работы с UIAutomator я не нашёл никаких проблем. Я думаю, что плюс в том, что он разрабатывается корпорацией Google вместе с Android и лучше интегрируется с ОС. Удобно, что не приходится настраивать и дополнительно устанавливать на компьютер какие-либо фреймворки, так как UIAutomator идет в составе c Android SDK. Минусом UIAutomator является только то, что он работает начиная с API Level 16+.
      • 0
        Кстати, UI Automator уже года полтора как не разрабатывается. Ну то есть формально он есть в сорцах SDK и андроида, но коммитов по UI Automator не делают.
    • 0
      UI Automator существенно отличается. Если robotium/expresso работают с View как с классами, то тут все проще. Вначале видимая область экрана на телефоне сохраняется в XML где каждый элемент представлен тегом — классом, текстом, описанием и прямоугольником. Тесты затем работают с этими тегами и прямоугольниками, а не с классами. Потому нельзя скажем узнать ID view, или скажем её цвет. Это кажется конечно костылем, но в этом есть и свой шарм. Например, можно тестировать прошивку и стандартные приложения.

      Я вот начал делать альтернативу автоматору — Lemur: bitbucket.org/zserge/lemur/overview
      Тоже использую дамп вьюх экрана в XML, но тесты пишу на Lua и запускаю значительно проще — «lemur.sh <имя теста.lua>». Где-то секунда уходит на запуск. Даже компилировать ничего не надо. И глюков меньше (напр., скролл в андроидах 4.1-4.2). Впереди еще долгий путь, но если кому подобный инструмент интересен — пишите, говорите какие фичи нужны и все такое.
  • 0
    А мне Espresso больше нравится. Он более понятен и проще на мой взгляд. Я работал только с ним, если есть кто работал более чем с одним фреймворком то напиши пожалуйста плюсы и минусы каждого.
    • +1
      Работал и работаю с calabash.
      Из плюсов — Ruby и кросс-платформенность. Cucumber и BDD тоже можно отнести к плюсам. Тесная интеграция с Xamarin, в будущем calabash станет плагином к Frank. Ну и конечно же активное сообщество, частые коммиты и релизы.

      Минусы свои тоже есть, например некоторые вещи сломались с выходом iOS 7, но со временем фиксят или находят обходные пути.

      Не могу напрямую сравнивать с UIAutomation для iOS или Android, не достаточно опыта работы с этими фреймворками. Пробовали сначала MonkeyTalk — calabash гораздо лучше.
      • 0
        Можно еще глянуть на appium, его проще с iOS интегрировать. И если есть тесты на calabsh, то на апиум их портировать можно мгновенно.
  • 0
    По весне сам озаботился вопросом автоматизации тестирования под Андроид. Жаль, что не нашел этйо статьи раньше.
    Документация Гугла весьма своеобразна: пропущена важные шаги. К примеру, какой проект вобще создавать надо и как?
    Коллега копал Robotium. Мне UIAutomator понравился тем, что не нужен доступ к исходному коду приложения, написание кода теста достаточно просто и очевидно, работает быстро.

    Одна трудность — отчет о работе. В статье об этом ни слова.
    Так как тест запускается из консоли, в код теста добавил функцию
    private void log(String message) {
    System.out.println(«TEST > » + message);
    }
    Она просто выводит в консоль нужное сообщение. Так хоть как-то можно знать, чем сейчас тест занят. Наверняка есть возможность писать в текстовый файл, но я над этим еще не думал.
    • 0
      Как это не написанно? Я написал про то, как собирать логи с теста, только вместа потока stdout я использую стандартный буфер Android. Я не стал подробно описывать, как именно вставлять это в код, лишь описал саму функцию и то, как читать логи с Android, но если вы посмотрите конечный код, что можете заметить, что были добавлены вызовы методов, которые логируют начало и конец запуска теста:

      Log.i("SendMessageTest", "Start SendMessage");
      findAndRunApp();
      sendMessage(toNumber, text);
      exitToMainWindow();
      Log.i("SendMessageTest", "End SendMessage");
      

      C таким же успехом мы можем добавить подробное логирование во все фукции скрипта, а потом, распарсив логи полученные с устройства, мы можем получить подробный отчет о всех действиях теста.
  • 0
    Начал использовать Espresso. Всё таки решил перейти на UIAutomator. В Espresso меньше функционала, и это не удивительно, ведь она вышла только пол года назад. А так же мало документации и примеров.
  • 0
    Спасибо за статью, очень интересно!
    Только есть один вопрос! Есть ли решения для тестинга приложений типа чата? Т.е. чтобы сценарий запускался сразу на двух телефонах, но проверял разные действия. На одном телефоне тест набирает сообщение, на другом ждет и проверяет, действительно ли оно пришло, ну все в таком роде
    • 0
      Спасибо!
      Честно говоря, не вставала подобная задача, но думаю, что это реально и делать надо примерно так:
      Для решения надо написать 2 сценария UIAutomator, один который будет заниматься отправкой, другой — приемом сообщений и один скрипт, который будет одновременно запускать эти 2 скрипта на 2-х телефонах.
      Думаю, что это не сложно реализовать.
      • 0
        Спасибо, буду копать )

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

Самое читаемое Разработка