Что есть robotium? Вообще это библиотека, которая представляет собой просто jar-файл, подключаемый к вашему тестовому проекту. Разработчики же данной библиотеки говорят "Этот как Selenium, только для Android". Начну я свой рассказа пожалуй с того, что из себя представляет Android проект.
Структура Android проекта
Приложение андроид состоит из нескольких основных ресурсов: это манифест приложения (xml файл), набор различных ресурсов и собственно сами исходники.
Что ж там есть в этом проекте то?
- директория gen - тут лежат файлы сгенерированные джавой.
- файл AndroidManifest.xml - файл манифеста приложения, предоставляет основную информацию о программе системе
- директория src - исходные коды приложения
- директория assets - произвольные файлы и подпапки
- директория res - этот каталог содержит ресурсы приложения. Как правило содержит подпапки drawable, anim, layout, menu, values, xml и raw.
Приложения в Android используют четыре основные компонента:
-Activities - это визуальный компонент приложения, который отвечает за UI. Приложение может содержать как несколько Activity, так и вовсе не содержать.
-Services - это то что выполняется пока приложение не находится в фокусе. Сервис может запускаться вместе с системой и "висеть" в фоне выполняя некоторые действия. Взаимодействовать сService можно посредством интерфейсов.
-Broadcast receivers - один из важнейших компонентов приложения. Broadcast receiver также как и сервис не имеет видимого интерфейса. Это что-то на подобие перехватчика событий
-Content providers - предоставляет определенные данные другим приложением. Эти данные могут храниться в файловой системе, SQLite базе данных, или еще где-то.
Основные методы жизненного цикла приложения
- protected void onCreate (Bundle savedInstanceState);
- protected void onStart();
- protected void onRestart();
- protected void onResume();
- protected void onPause();
- protected void onStop();
- protected void onDestroy();
onCreate
Вызывается при создании активности. Этот метод принимает один параметр — объект Bundie, содержащий предыдущее состояние активности (если это состояние было сохранено). Система может запускать и останавливать текущие окна в зависимости от происходящих событий. Android вызывает метод onCreate() после запуска или перезапуска Activity. Внутри этого метода настраивают статический интерфейс активности. Инициализирует статические данные активности, связывают данные со списками и т. д.. Связывает с необходимыми данными и ресурсами. Задает внешний вид через методsetContentView().
onStart
За onCreate() всегда следует вызов onStart(), но перед onStart() не обязательно должен идти onCreate(), так как onStart() может вызываться и для возобновления работы приостановленного приложения (приложение останавливается методом onStop()). При вызове onStart() окно еще не видно пользователю, но вскоре будет видно. Вызывается непосредственно перед тем, как активность становится видимой пользователю. Сопровождается вызовом метода onResume(), если активность получает передний план, или вызовом метода onStop(), если становится скрытой;
onResume
Метод onResume() вызывается после onStart(), даже когда окно работает в приоритетном режиме и пользователь может его наблюдать. В этот момент пользователь взаимодеиствует с созданным вами окном. Приложение получает монопольные ресурсы. Запускает воспроизведение анимации, аудио и видео. Также может вызываться после onPause().
onPause
Когда пользователь решает перейти к работе с новым окном, система вызовет для прерываемого окна метод onPause(). По сути происходит свертывание активности. Сохраняет незафиксированные данные. Деактивирует и выпускает монопольные ресурсы. Останавливает воспроизведение видео, аудио и анимацию. От onPause() можно перейти к вызову либо onResume(), либо onStop().
onStop
Метод onStop() вызывается, когда окно становится невидимым для пользователя. Это может произойти при ее уничтожении, или если была запущена другая активность (существующая или новая), перекрывшая окно текущей активности. Всегда сопровождает любой вызов метода onRestart(), если активность возвращается, чтобы взаимодействовать с пользователем, или метода onDestroy(), если эта активность уничтожается.
onRestart
Если окно возвращается в приоритетный режим после вызова onStop(), то в этом случае вызывается метод onRestart(). Т.е. вызывается после того, как активность была остановлена и снова была запущена пользователем. Всегда сопровождается вызовом метода onStart()
onDestroy
Метод вызывается по окончании работы активности, при вызове метода finish() или в случае, когда система уничтожает этот экземпляр активности для освобождения ресурсов. Эти два сценария уничтожения можно определить вызовом метода isFinishing(). Вызывается перед уничтожением активности. Это последний запрос, который получает активность от системы. Если определенное окно находится в верхней позиции в стеке, но невидимо пользователю и система решает завершить это окно, вызывается метод onDestroy(). В этом случае метод удаляет все статические данные активности. Отдает все используемые ресурсы.
Из всего выше перечисленного нам понадобится только джава класс R.
Напишем примерочные тесты на Robotium
Для начала скачаем саму библиотеку Robotiumа
После установим Android SDK на компик, так же установим eclipse и соответствующий плагин для android, который можно найти обычным поиском в eclipse market. Ну и естественно все настроим.
После того как все настроено и установлено, создадим стандартный пример приложения андроид из Android Project Sample, который представляет из себя обычный блокнот NotePad
Ну и запустим его
Видим что открылся эмулятор и все запустилось и работает
После чего создадим Android JUnit Test Project, к которому мы подключим нашу библиотеку robotium.jar. Тестовый проект создадим на основе android приложения NodePad
Ну и в тестовом классе напишем следующий код:
import com.example.android.notepad.NotesList;
import com.jayway.android.robotium.solo.Solo;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.Smoke;
public class NotePadTest extends ActivityInstrumentationTestCase2<NotesList>{
private Solo solo;
public NotePadTest() {
super("com.example.android.notepad", NotesList.class);
}
public void setUp() throws Exception {
solo = new Solo(getInstrumentation(), getActivity());
}
@Smoke
public void testAddNote() throws Exception {
solo.clickOnMenuItem("Add note");
//Assert that NoteEditor activity is opened
solo.assertCurrentActivity("Expected NoteEditor activity", "NoteEditor");
//In text field 0, add Note 1
solo.enterText(0, "Archik 1");
solo.goBack();
//Clicks on menu item
solo.clickOnMenuItem("Add note");
//In text field 0, add Note 2
solo.enterText(0, "Archik 2");
//Go back to first activity named "NotesList"
solo.goBackToActivity("NotesList");
boolean expected = true;
boolean actual = solo.searchText("Archik 1") && solo.searchText("Archik 2");
//Assert that Note 1 & Note 2 are found
assertEquals("Archik 1 and/or Archik 2 are not found", expected, actual);
}
@Smoke
public void testEditNote() throws Exception {
// Click on the second list line
solo.clickInList(2);
// Change orientation of activity
solo.setActivityOrientation(Solo.LANDSCAPE);
// Change title
solo.clickOnMenuItem("Edit title");
//In first text field (0), add test
solo.enterText(0, " test");
solo.goBackToActivity("NotesList");
boolean expected = true;
// (Regexp) case insensitive
boolean actual = solo.searchText("(?i).*?archik 1 test");
//Assert that Note 1 test is found
assertEquals("Archik 1 test is not found", expected, actual);
}
@Smoke
public void testRemoveNote() throws Exception {
//(Regexp) case insensitive/text that contains "test"
solo.clickOnText("(?i).*?test.*");
//Delete Note 1 test
solo.clickOnMenuItem("Delete");
//Note 1 test & Note 2 should not be found
boolean expected = false;
boolean actual = solo.searchText("Archik 1 test");
//Assert that Note 1 test is not found
assertEquals("Archik 1 Test is found", expected, actual);
solo.clickLongOnText("Archik 2");
//Clicks on Delete in the context menu
solo.clickOnText("(?i).*?Delete.*");
actual = solo.searchText("Archik 2");
//Assert that Note 2 is not found
assertEquals("Archik 2 is found", expected, actual);
}
@Override
public void tearDown() throws Exception {
//Robotium will finish all the activities that have been opened
solo.finishOpenedActivities();
}
}
После чего запустим наши тесты
Главной фишкой является эдакий роботик-муравей под названием Solo, которого мы и создаем в начале нашего тестового класса
Именно он, по средствам своих методов (Robotium API) будет деркать, тыкать, кликать и вводить текст в нашем приложении. Инструментарий у робота богат.
Методы для манипулирования компонентами УИ:
clickOnScreen
clickOnMenuItem
clickOnImageButton
clickOnRadioButton
clickOnText
clickOnToggleButton
clickOnView
clickOnImage
clickOnCheckBox
clickOnButton
clickOnEditText
и др.
Различные скроллы и драг
scrollDown
scrollDownList(
scrollToSide
scrollUp
scrollUpList
drag
и др.
Ожидалки на появление чего-то или кого-то, или просто заснуть на время
waitForDialogToClose
waitForText
waitForView
sleep
и др.
Как же робот находит элементы? Так же как и в Selenium по локаторам, а точнее id элементам.
Возвращаясь к началу топика, я заострил внимание на класс в Android приложении R.java.
Он содержит пространство имен под названием id. В этом пространстве содержаться айдишки всех элементов графического интерфейса нашего приложения. По принципу whitebox testing мы можем обращаться к ним в наших тестах solo.getView(R.id.TextView01);
Так же можно искать элементы и методом черной коробки. Solo робот сам идентифицирует компоненты юая обычными числами начиная с 0. Т.е. если у нас на экране 2 поля ввода текста, то ввести текст в них мы сможем вот так:
//Enter 10 in first editfield
solo.enterText(0, "10");
//Enter 20 in second editfield
solo.enterText(1, "20");
Так же мы можем искать элементы по тексту, даже используя регулярные выражения (примеры кода выше). Таким образом мы можем автоматизировать тестирование даже не имея исходников приложения, достаточно иметь apk файл. Есть возможность тестирования на реальном девайсе так же.
Я не пишу и не хочу писать код. Где рекордер?
Не вопрос. Рекордер есть, при чем в виде плагина к эклипсу под название Testdroid Recorder
Установим этот плагин и зарегаемся на их сайте. После чего создадим Testdroid Test в эклипсе в уже созданном тестовом проекте андроид приложения.
Выберим приложение к которому будем записывать тест и за одно куда его сохранять
После чего подконнектимся к своему аккаунту созданному на сайте Testdroid
После чего запустится эмулятор с нашим приложением и консоль, в которой будут отображаться записываемые нами действия
Во время записи есть возможность сэмулировать действие PrtScr андроид экрана :)
После записи мы можем отредактировать наш код. На самом деле рекордер записывает много лишних действий, в основном ожидалок, которые можно закомментарить
Отредактированный тест можно запускать, так же как и junit тест. Собственно весь testdroid и построен на основе robotium.
Так же его плагин позволяет нам запускать наши тесты не у себя на машине а в "облаках"
The end