android test driven development

52
Android Test Driven Development Carlo Codega [email protected] Milano, 21 Maggio 2010

Upload: sazilla

Post on 02-Jul-2015

1.341 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Android Test Driven Development

Android Test Driven Development

Carlo [email protected]

Milano, 21 Maggio 2010

Page 2: Android Test Driven Development

Agenda

• Introduzione al Test Driven Development• Unit testing vs Functional testing

• Unit testing:– Componenti indipendenti da Android: JUnit

– Componenti dipendenti da Android, isolabili dal resto dell’applicazione– Macro componenti: Application, Activity, Service e ContentProvider– Generazione di test suite

– Esecuzione di test suite con adb (Android Debug Bridge)

• Functional testing:– Componenti che hanno interfaccia utente: Activity

– Integration testing sull’intera applicazione: Instrumentation

• Stress testing: Monkey tool

Page 3: Android Test Driven Development

Introduzione al TDDPerché fare TDD in Android:

1. Scrivere codice che funziona :)

2. Scrivere i test prima dell’implementazione funzionale

3. Pensare al design dell’applicazione prima dell’implementazione

reale

4. Evitare i bachi di regression. Scrivere specifici test case per ogni

bug trovato

5. Facilitare il refactoring del codice

Page 4: Android Test Driven Development

Unit testing vs Functional testing

• Unit tests:– Servono a verificare che tutte le componenti di un programma

funzionano correttamente

– Vengono fatti solitamente dagli stessi sviluppatori che hannoimplementato il codice

– Vengono scritti in un determinato linguaggio di programmazione

• Funcional tests (o acceptance tests):– Servono a verificare se un programma risponde ai requisiti dell’utente– Vengono fatti da persone di QA (Quality Assurance)

– Vengono scritti in un linguaggio di alto livello

Page 5: Android Test Driven Development

Unit testing vs Functional testingUnit testing Functional testing

Page 6: Android Test Driven Development

Android testing framework

• Android integra al suo interno un framework per il testing:

– Package android.test

• Basato su JUnit 3

• Supporta:

– Unit testing: TestCase, AndroidTestCase

– Functional testing: InstrumentationTestCase

• Include delle utility per facilitare la creazione di test suite

• Android Debug Bridge (adb) per eseguire i test su emulatore o

device reale

Page 7: Android Test Driven Development

• Componenti indipendenti da Android: JUnit classico

• Aiuta a separare la logica dal contesto

• Esempio: MorseCode

Unit testing

TestCase

Page 8: Android Test Driven Development

Unit testing

• MorseCode (Android SDK)

Page 9: Android Test Driven Development

Unit testing

Activity

MorseCode MorseCodeConverter

= dipendende da Android

= indipendende da Android

Page 10: Android Test Driven Development

Unit testingclass MorseCodeConverter {

static final long SPEED_BASE = 100;static final long DOT = SPEED_BASE;static final long DASH = SPEED_BASE * 3;static final long GAP = SPEED_BASE;

/** The characters from 'A' to 'Z' */private static final long[][] LETTERS = new long[][] {

/* A */ new long[] { DOT, GAP, DASH },/* B */ new long[] { DASH, GAP, DOT, GAP, DOT, GAP, DOT },/* C */ new long[] { DASH, GAP, DOT, GAP, DASH, GAP, DOT },

...};

/** The characters from '0' to '9' */private static final long[][] NUMBERS = new long[][] {

/* 0 */ new long[] { DASH, GAP, DASH, GAP, DASH, GAP, DASH }, /* 1 */ new long[] { DOT, GAP, DASH, GAP, DASH, GAP, DASH }, ...

};

Page 11: Android Test Driven Development

Unit testing

/** Return the pattern data for a given character */static long[] pattern(char c) {...

}

/** Return the pattern data for a given string */static long[] pattern(String str) {...

}}

Page 12: Android Test Driven Development

Unit testing

TestCase

MorseCodeConverterTest

Page 13: Android Test Driven Development

Unit testing

public class MorseCodeConverterTest extends TestCase {

public void testCharacterS() throws Exception {

long[] expectedBeeps = { MorseCodeConverter.DOT, MorseCodeConverter.DOT, MorseCodeConverter.DOT, MorseCodeConverter.DOT,

MorseCodeConverter.DOT };

long[] beeps = MorseCodeConverter.pattern('s');

assertEquals(expectedBeeps, beeps);}

}

Page 14: Android Test Driven Development

• Componenti dipendenti da Android:

• Viene utilizzato per le componenti che richiedono l’accesso alContext:– eseguire Intent, lanciare Activity e Service– accedere al File System e ContentProvider

• Esempio: AndroidKeyValueStore

Unit testing

AndroidTestCase

Page 15: Android Test Driven Development

Unit testing

AndroidKeyValueStore Context

Page 16: Android Test Driven Development

Unit testingpublic class AndroidKeyValueStore {

private SQLiteDatabase dbStore;private DatabaseHelper mDatabaseHelper;

public AndroidKeyValueStore(Context context) {mDatabaseHelper = new DatabaseHelper(context,

"dbname”, "tablename");open();// Create tabledbStore.execSQL(...);close();

}

public void open() {if(dbStore == null) {

dbStore = mDatabaseHelper.getWritableDatabase();}

}

Page 17: Android Test Driven Development

Unit testing

public void close() {dbStore.close();dbStore = null;

}

public void put(String key, String value) { dbStore.execSQL(...);

}

public String get(String key) {dbStore.execSQL(...);

}}

Page 18: Android Test Driven Development

Unit testing

AndroidTestCase

AndroidKeyValueStoreTest

Page 19: Android Test Driven Development

Unit testingpublic class AndroidKeyValueStoreTest extends AndroidTestCase {

private AndroidKeyValueStore store;

protected void setUp() {store = new AndroidKeyValueStore(getContext()); store.load();

}

protected void tearDown() {store.close();

}

public void testPutGet() throws Exception {store.put("aKey", "aValue");String value = store.get(aKey);assertEquals(value, "aValue");

}}

Page 20: Android Test Driven Development

Unit testing

• Test di macro componenti in ambiente controllato:– Controllo globale sul ciclo di vita del componente

– Application:• ApplicationTestCase <T extends Application>

– Activity (functional test):• ActivityTestCase

• ActivityInstrumentationTestCase<T extends Activity> (deprecato)

• ActivityInstrumentationTestCase2<T extends Activity>

– Service:• ServiceTestCase<T extends Service>

– ContentProvider:• ProviderTestCase <T extends ContentProvider> (deprecato)

• ProviderTestCase2 <T extends ContentProvider>

Page 21: Android Test Driven Development

Unit testing

AndroidTestCase

ApplicationTestCase ServiceTestCase ProviderTestCase

Page 22: Android Test Driven Development

Unit testing

InstrumentationTestCase

ActivityInstrumentationTestCase

Instrumentation = Functional testing !!

Page 23: Android Test Driven Development

Unit testing

Generazione di test suite per raggruppare e classificare i TestCase:

TestSuite

MyTestSuite TestSuiteBuilder

Page 24: Android Test Driven Development

Unit testing

Includere tutti i test case appartenenti ad un package:

public class SomeTests extends TestSuite { public static Test suite() {

return new TestSuiteBuilder(SomeTests.class) .includePackages("com.myapp.package1", "com.myapp.package2")

.build(); }

}

public class AllTests extends TestSuite { public static Test suite() {

return new TestSuiteBuilder(AllTests.class) .includeAllPackagesUnderHere().build();

}}

Page 25: Android Test Driven Development

Unit testing

Struttura dei test:

> AndroidManifest.xml src

restests > AndroidManifest.xml

src > com > myapp > AllTests.javaSomeTests.javapackage1 > TestCase1.java

TestCase2.javapackage2 > AndroidTestCase1.java

AndroidTestCase2.java

Page 26: Android Test Driven Development

Unit testing

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.myapp.tests">

<application><uses-library android:name="android.test.runner” />

</application>

<instrumentation android:name="android.test.InstrumentationTestRunner”android:targetPackage="com.myapp” android:label=”MyApp Tests"/>

</manifest>

Page 27: Android Test Driven Development

Unit testingEseguire test suite con adb (Android Debug Bridge):

• Eseguire tutti i test:– adb shell am instrument -w com.myapp/android.test.InstrumentationTestRunner

• Eseguire una singola TestSuite o un singolo TestCase:– adb shell am instrument -w -e class com.myapp.SomeTests

com.myapp/android.test.InstrumentationTestRunner– adb shell am instrument -w -e class com.myapp.package1.TestCase1

com.myapp/android.test.InstrumentationTestRunner

• Eseguire solo unit tests:– Test che non derivano da InstrumentationTestCase– adb shell am instrument -w -e unit true

com.myapp/android.test.InstrumentationTestRunner

Page 28: Android Test Driven Development

Unit testing

• Classificare per dimensione:– adb shell am instrument -w -e size small

com.myapp/android.test.InstrumentationTestRunner

– Classificati in base alle annotazioni:• @SmallTest > -e size small

• @MediumTest > -e size medium

• @LargeTest > -e size large

• Output da adb:

com.myapp.package1.TestCase1:........com.myapp.package1.TestCase2:.....com.myapp.package2.AndroidTestCase1:.........com.myapp.package2.AndroidTestCase1:......Test results for InstrumentationTestRunner=............................Time: 124.526

OK (28 tests)

Page 29: Android Test Driven Development

Unit testing

• Eseguire i test direttamente dall’emulatore:

Page 30: Android Test Driven Development

Functional testing

• Componenti che hanno interfaccia utente: Activity

• Integration testing sull’intera applicazione:

ActivityInstrumentationTestCase

Instrumentation

Page 31: Android Test Driven Development

Functional testing

• Componenti che hanno interfaccia utente: Activity

InstrumentationTestCase

ActivityInstrumentationTestCase

Page 32: Android Test Driven Development

Functional testing

ActivityInstrumentationTestCase2<T extends Activity>

• Testing isolato per una singola Activity

• Tramite l’oggetto Instrumentation si può interagire con l’interfacciautente

• È possibile utilizzare le TouchUtils per simulare eventi touch

Page 33: Android Test Driven Development

Functional testing

• Instrumentation:– sendCharacterSync(int keyCode)

– sendKeySync(KeyEvent event)

– sendPointerSync(MotionEvent event)

– sendStringSync(String text)

– setInTouchMode(boolean inTouch)

Page 34: Android Test Driven Development

Functional testing

• TouchUtils:

– clickView(InstrumentationTestCase test, View v)

– drag(InstrumentationTestCase test, float fromX, float toX, float fromY,float toY, int stepCount)

– longClickView(InstrumentationTestCase test, View v)

– scrollToBottom(InstrumentationTestCase test, ViewGroup v)

– tapView(InstrumentationTestCase test, View v)

Page 35: Android Test Driven Development

Functional testing

• Calculator

Page 36: Android Test Driven Development

Functional testing

Activity

Calculator Logic

Page 37: Android Test Driven Development

Functional testing

ActivityInstrumentationTestCase<Calculator>

CalculatorTest

Page 38: Android Test Driven Development

Functional testingpublic class CalculatorTest extends

ActivityInstrumentationTestCase<Calculator> {

Calculator mActivity = null;Instrumentation mInst = null;

public CalculatorTest() { super("com.android.calculator2", Calculator.class);

}

protected void setUp() throws Exception {super.setUp();mActivity = getActivity();mInst = getInstrumentation();

}

Page 39: Android Test Driven Development

Functional testingprivate void press(int keycode) {

mInst.sendKeyDownUpSync(keycode);}

private boolean tap(int id) {View view = mActivity.findViewById(id);if(view != null) {

TouchUtils.clickView(this, view);return true;

}return false;

}

private String displayVal() {CalculatorDisplay display = (CalculatorDisplay)

mActivity.findViewById(R.id.display);EditText box = (EditText)display.getCurrentView();return box.getText().toString();

}

Page 40: Android Test Driven Development

Functional testingpublic void testPressSomeKeys() {

// Make sure that we clear the output press(KeyEvent.KEYCODE_ENTER); press(KeyEvent.KEYCODE_CLEAR);

// 3 + 4 * 5 => 23press(KeyEvent.KEYCODE_3); press(KeyEvent.KEYCODE_PLUS);press(KeyEvent.KEYCODE_4);press(KeyEvent.KEYCODE_9 | KeyEvent.META_SHIFT_ON);press(KeyEvent.KEYCODE_5);press(KeyEvent.KEYCODE_ENTER);

assertEquals(displayVal(), "23");}

Page 41: Android Test Driven Development

Functional testingpublic void testTapSomeButtons() {

// Make sure that we clear the output tap(R.id.equal);tap(R.id.del);

// 567 / 3 => 189tap(R.id.digit5);tap(R.id.digit6);tap(R.id.digit7);tap(R.id.div);tap(R.id.digit3);tap(R.id.equal);

assertEquals(displayVal(), "189");

Page 42: Android Test Driven Development

Functional testing

// make sure we can continue calculations also // 189 - 789 => -600tap(R.id.minus);tap(R.id.digit7);tap(R.id.digit8);tap(R.id.digit9);tap(R.id.equal);

assertEquals(displayVal(), ”-600"); }

}

• Le primitive sono semplici e astratte: press, tap, assert

Page 43: Android Test Driven Development

Functional testing

• Integration testing sull’intera applicazione:– Definire un linguaggio di alto livello per testare l’applicazione dal

punto di vista dell’utente

– Implementare un interprete del linguaggio per tradurre i comandi inazioni effettuate tramite l’oggetto Instrumentation

• Implementare un custom Instrumentation ci permette di avere iltotale controllo sull’applicazione

• Il manifest di test deve contenere una copia del manifestdell’applicazione originale e la definizione di un nuovoInstrumentation per poter eseguire i test di integrazione

Page 44: Android Test Driven Development

Functional testingAndroidManifest.xml

<manifest package="com.myapp">

<application android:label=”MyApp"><activity android:name="com.myapp.Activity" /><service android:name="com.myapp.Service" /><uses-library android:name="android.test.runner" />

</application>

<instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.myapp"android:label="MyApp Tests" />

<instrumentation android:name="com.myapp.CustomInstrumentation” android:targetPackage="com.myapp"

android:label="MyApp Integration Tests" />

</manifest>

Page 45: Android Test Driven Development

Functional testing

• Definire un linguaggio di alto livello per simulare le azionidell’utente (esempio: Funambol integration tests):– StartMainApp()

– KeyPress(String command, int count)– WriteString(String text)

– CreateEmptyContact()– DeleteAllContacts()

– Include(String scriptUrl)

• Linguaggio utilizzato dal QA per scrivere i test partendo dagliacceptance tests

Page 46: Android Test Driven Development

Functional testing

Instrumentation

CustomInstrumentation CommandRunner Robot

Page 47: Android Test Driven Development

Functional testing

• CustomInstrumentation:– Carica lo script di test e lo esegue tramite il CommandRunner

• CommandRunner:– Interpreta lo script

– Traduce ogni comando in azione eseguita tramite il Robot

• Robot:– Esegue delle azioni sull’applicazione tramite un riferimento

all’oggetto Instrumentation

Page 48: Android Test Driven Development

Functional testing

• Acceptance test case:1. “On Device add a record in the Contacts section filling in all possible

fields”

2. “Fire synchronization and wait for sync complete”

3. “Check the new contact is added to the server”

Page 49: Android Test Driven Development

Functional testing# Create on Device side a new Contact (Andrea Bianchi)CreateEmptyContact()SetContactField(FirstName,"Andrea")SetContactField(LastName, "Bianchi")SetContactField(TelHome, "0382665765979")SetContactField(TelCell, "3445674")SetContactField(Birthday, "1987-09-13")...SaveContact()

# Fire the synchronization and wait that is completeKeyPress(KeyFire)WaitForSyncToComplete(2,20)

# Verify Exchanged DataCheckExchangedData("Contacts",1,0,0,0,0,0)RefreshServer()

# Verify if the contact is added on the ServerCheckNewContactOnServer("Andrea", "Bianchi", true)

Page 50: Android Test Driven Development

Functional testing

• Eseguire i test di integrazione:

– adb shell am instrument [options] -w

com.myapp/com.myapp.CustomInstrumentation

– È possibile specificare dei parametri aggiuntivi tramite il flag -e (extra)

– I parametri vengono passati tramite una Bundle attraverso il metodo

onCreate() dell’oggetto Instrumentation:

• public void onCreate(Bundle arguments)

– Dall’emulatore:

• Dev Tools / Instrumentation / MyApp Integration Tests

Page 51: Android Test Driven Development

Stress testing

• Monkey tool:– Applicazione command line che può essere eseguita su emulatore o

device reale

– Genera eventi utente pseudo-casuali:• Click

• Touch• Gestures

– Configurabile:• -p <allowed-package-name>: lista dei package sui quali è possibile

generare eventi

– Usage:• adb shell monkey [options] <event-count>

Page 52: Android Test Driven Development

Q&A