android test driven development
TRANSCRIPT
![Page 2: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/2.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/3.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/4.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/5.jpg)
Unit testing vs Functional testingUnit testing Functional testing
![Page 6: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/6.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/7.jpg)
• Componenti indipendenti da Android: JUnit classico
• Aiuta a separare la logica dal contesto
• Esempio: MorseCode
Unit testing
TestCase
![Page 8: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/8.jpg)
Unit testing
• MorseCode (Android SDK)
![Page 9: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/9.jpg)
Unit testing
Activity
MorseCode MorseCodeConverter
= dipendende da Android
= indipendende da Android
![Page 10: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/10.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/11.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/12.jpg)
Unit testing
TestCase
MorseCodeConverterTest
![Page 13: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/13.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/14.jpg)
• 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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/15.jpg)
Unit testing
AndroidKeyValueStore Context
![Page 16: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/16.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/17.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/18.jpg)
Unit testing
AndroidTestCase
AndroidKeyValueStoreTest
![Page 19: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/19.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/20.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/21.jpg)
Unit testing
AndroidTestCase
ApplicationTestCase ServiceTestCase ProviderTestCase
![Page 22: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/22.jpg)
Unit testing
InstrumentationTestCase
ActivityInstrumentationTestCase
Instrumentation = Functional testing !!
![Page 23: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/23.jpg)
Unit testing
Generazione di test suite per raggruppare e classificare i TestCase:
TestSuite
MyTestSuite TestSuiteBuilder
![Page 24: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/24.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/25.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/26.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/27.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/28.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/29.jpg)
Unit testing
• Eseguire i test direttamente dall’emulatore:
![Page 30: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/30.jpg)
Functional testing
• Componenti che hanno interfaccia utente: Activity
• Integration testing sull’intera applicazione:
ActivityInstrumentationTestCase
Instrumentation
![Page 31: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/31.jpg)
Functional testing
• Componenti che hanno interfaccia utente: Activity
InstrumentationTestCase
ActivityInstrumentationTestCase
![Page 32: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/32.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/33.jpg)
Functional testing
• Instrumentation:– sendCharacterSync(int keyCode)
– sendKeySync(KeyEvent event)
– sendPointerSync(MotionEvent event)
– sendStringSync(String text)
– setInTouchMode(boolean inTouch)
![Page 34: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/34.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/35.jpg)
Functional testing
• Calculator
![Page 36: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/36.jpg)
Functional testing
Activity
Calculator Logic
![Page 37: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/37.jpg)
Functional testing
ActivityInstrumentationTestCase<Calculator>
CalculatorTest
![Page 38: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/38.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/39.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/40.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/41.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/42.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/43.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/44.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/45.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/46.jpg)
Functional testing
Instrumentation
CustomInstrumentation CommandRunner Robot
![Page 47: Android Test Driven Development](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/47.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/48.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/49.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/50.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/51.jpg)
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](https://reader034.vdocument.in/reader034/viewer/2022042715/5594b0681a28ab0a648b45d1/html5/thumbnails/52.jpg)
Q&A