Введение
Я уверен, что современных программистов не нужно убеждать использовать юнит тесты. И если при разработке приложений под android, модули не связанные с ним, мы можем протестировать с помощью знакомого всем JUnit'а (не всегда без костылей правда), то как тестировать user interface и использующие его классы? В этом нам помогут инструменты фреймворка android.test.
В данной статье мы напишем небольшое android приложение и протестируем его. Для работы нам будут нужны установленные jdk, android sdk, Eclipse и ADT плагин.
Часть первая. SpeedConverter.
Наше примитивное приложение будет переводить «километры в час» в «метры в секунду» и наоборот, а также выдавать сообщение Error!, если нужное значение не удается вычислить. Пользователь будет вводить данные о скорости в любой EditText и в другом получать результат. Более подробно на нем останавливаться не будем.
XML layout формы:
xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="12dp">
<TextView
android:id="@+id/textKmPerHour"
android:text="@string/kmh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<EditText
android:id="@+id/editKmPerHour"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:inputType="numberDecimal"
android:numeric="decimal"/>
<TextView
android:id="@+id/textMetersPerSec"
android:text="@string/ms"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="4dp"/>
<EditText
android:id="@+id/editMetersPerSec"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:inputType="numberDecimal"
android:numeric="decimal"/>
LinearLayout>
* This source code was highlighted with Source Code Highlighter.
Далее устанавливаем listener для Kilometers per hour EditText’а и при наступлении события вычисляем скорость. Нюанс, что onKey() не наступает при активной software клавиатуре мы учтем позже в тестах. Второй EditText работает по аналогии.
// setup listener for Kilometers per hour EditText
editKmPerHour.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP) {
try {
double kmPerHour = Double.parseDouble(editKmPerHour
.getText().toString());
double meterPerSec = kmPerHour * 0.2777777777777778;
editMeterPerSec.setText(new Double(meterPerSec)
.toString());
} catch (NumberFormatException e) {
editMeterPerSec.setText(R.string.errorMsg);
Log.d(LOG, "e:" + e);
}
return true;
}
return false;
}
});
* This source code was highlighted with Source Code Highlighter.
Теперь перейдем к тестированию.
Часть вторая. SpeedConverterTest.
Android Test Project
В первой части мы создали приложение SpeedConverter. Сейчас для него нам нужно создать новое тестовое приложение. Идем в меню eclipse’a: File -> New -> Projects... в появившемся окне выбираем Android -> Android Test Project, нажимаем Next и видим такое окно:
Зададим следующие значения:
- Test Project Name: SpeedConverterTest
- Test Target: устанавливаем «An existing Android project» и выбираем наше проект SpeedConverter
- Application name и Package name подставятся автоматически
Создание Test Case класса
В директории src/ нашего проекта мы создадим новый класс New > Class. Назовем его SpeedConverterTest и в качестве SuperClass’а укажем android.test.ActivityInstrumentationTestCase2. Наш диалог будет выглядеть так:
Теперь немного подробней об ActivityInstrumentationTestCase2. Данный класс спроектирован для тестирования activities в android приложении. Он может создавать activity (используя InstrumentationTestCase.launchActivity()), запускать тесты в UI thread и позволяет посылать нам различные mock Intents в нашу activity. В качастве параметра мы должны будем передать SpeedConverter.
Добавим test case конструктор который будет использоваться тестирующим фреймворком. В качестве параметров мы покажем какое android приложение будет тестироваться.
public SpeedConverterTest() {
super("com.pyjioh", SpeedConverter.class);
}
Далее, знакомый по JUnit’у метод setUp(), который вызывается перед запуском тестов, будем инициализировать в нем переменные.
@Override
protected void setUp() throws Exception {
// TODO Auto-generated method stub
super.setUp();
activity = getActivity();
editKmPerHour = (EditText) activity
.findViewById(com.pyjioh.R.id.editKmPerHour);
editMeterPerSec = (EditText) activity
.findViewById(com.pyjioh.R.id.editMetersPerSec);
}
Начинаем добавлять тесты, они будут простыми дабы показать как взаимодействовать с UI, первый будет проверять — создалась и запустилась ли наша SpeedConverter activity, а также имеем ли мы доступ к нашим контролам.
public void testControlsCreated() {
assertNotNull(activity);
assertNotNull(editKmPerHour);
assertNotNull(editMeterPerSec);
}
Используя метод assertOnScreen() из ViewAsserts проверяем видны ли наши контролы.
public void testControlsVisible() {
ViewAsserts.assertOnScreen(editKmPerHour.getRootView(), editMeterPerSec);
ViewAsserts.assertOnScreen(editMeterPerSec.getRootView(), editKmPerHour);
}
Запускается ли приложение с пустыми EditText’ми.
public void testStartingEmpty() {
assertEquals("editKmPerHour is not empty", "", editKmPerHour.getText()
.toString());
assertEquals("editMeterPerSec is not empty", "", editMeterPerSec
.getText().toString());
}
Теперь добавим тест, проверяющий правильность перевода км/с в м/с. В нем мы используем TouchUtils.tapView(..) который симулирует касание по нужному нам контролу, а KeyEvent.KEYCODE_BACK убирает software клавиатуру. Метод void testConvertMsToKmh() выглядит аналогично.
public void testConvertKmhToMs() {
TouchUtils.tapView(this, editKmPerHour);
sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_1, KeyEvent.KEYCODE_0,
KeyEvent.KEYCODE_0);
double meterPerSec;
try {
meterPerSec = Double.parseDouble(editMeterPerSec.getText()
.toString());
} catch (Exception e) {
meterPerSec = -1;
}
assertTrue("100 km/h is not 27.778 m/s", meterPerSec > 27.7
&& meterPerSec < 27.8);
}
Последний тест проверяет — получаем ли мы сообщение об ошибке.
public void testGetError() {
TouchUtils.tapView(this, editKmPerHour);
sendKeys(KeyEvent.KEYCODE_1, KeyEvent.KEYCODE_DEL);
assertEquals("Must be Error!",
activity.getString(com.pyjioh.R.string.errorMsg),
editMeterPerSec.getText().toString());
}
Запустим наше тестовое приложение: Run As > Android JUnit Test. На видео ниже показано как это будет выглядеть.
Таким образом, мы протестировали функционал нашего несерьезного приложения.
Что же еще?
Да, собственно, все остальное. Возможности фреймворка android.test намного шире. Например, класс ActivityUnitTestCase в отличие от ActivityInstrumentationTestCase2 позволяет тестировать activity изолированно, используя mock Context или Application. На официальном сайте developer.android.com можно познакомиться со всеми инструментами тестирования.
Исходный текст SpeedConverter и SpeedConverterTest лежит на code.google.com
©HabraHabr.ru