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

в 9: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>

    Где <name> — имя проекта, который создавался для теста UIAutomator (в нашем случае: SendMessage), <target-id> — выбор устройства и Android API Level (можно получить список установленных устройств командой: <android-sdk>/tools/android list targets) и <path> — путь к директории с проектом.

  2. Необходимо экспортировать переменную окружения ANDROID_HOME:
    • Windows:
      set ANDROID_HOME=<path_to_your_sdk>
    • Unix:
      export ANDROID_HOME=<path_to_your_sdk>
  3. Заходим в директорию с проектом, в которой лежит сгенерированный на шаге 1 файл build.xml и выполняем команду:
    $ ant build
  4. Копируем собранный JAR файл на устройство с помощью команды adb push:
    $ adb push <path_to_output_jar> /data/local/tmp/

    Для нашего случая:

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

Заключение

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

Автор: echuraev

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js