Android Test Driven Development
Upcoming SlideShare
Loading in...5
×
 

Android Test Driven Development

on

  • 3,687 views

 

Statistics

Views

Total Views
3,687
Views on SlideShare
3,127
Embed Views
560

Actions

Likes
3
Downloads
74
Comments
0

24 Embeds 560

http://sazilla.blogspot.com 333
http://www.whymca.org 56
http://sazilla.blogspot.it 55
http://conference2010.whymca.org 27
http://www.slideshare.net 19
http://sazilla.blogspot.fr 16
http://sazilla.blogspot.de 13
http://sazilla.blogspot.com.es 8
http://sazilla.blogspot.ru 6
http://translate.googleusercontent.com 3
http://sazilla.blogspot.ch 3
http://sazilla.blogspot.be 2
http://sazilla.blogspot.cz 2
http://sazilla.blogspot.co.uk 2
http://sazilla.blogspot.co.at 2
http://www.cesvin.net 2
http://sazilla.blogspot.in 2
http://www.ig.gmodules.com 2
http://sazilla.blogspot.ca 2
http://sazilla.blogspot.tw 1
http://sazilla.blogspot.com.ar 1
http://sazilla.blogspot.mx 1
http://sazilla.blogspot.co.il 1
http://sazilla.blogspot.dk 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Android Test Driven Development Android Test Driven Development Presentation Transcript

    • Android Test Driven Development Carlo Codega carlo.codega@funambol.com Milano, 21 Maggio 2010
    • 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
    • Introduzione al TDD Perché 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
    • 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 hanno implementato 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
    • Unit testing vs Functional testing Unit testing Functional testing
    • 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
    • Unit testing • Componenti indipendenti da Android: JUnit classico TestCase • Aiuta a separare la logica dal contesto • Esempio: MorseCode
    • Unit testing • MorseCode (Android SDK)
    • Unit testing = dipendende da Android Activity = indipendende da Android MorseCode MorseCodeConverter
    • Unit testing class 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 }, ... };
    • 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) { ... } }
    • Unit testing TestCase MorseCodeConverterTest
    • 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); } }
    • Unit testing • Componenti dipendenti da Android: AndroidTestCase • Viene utilizzato per le componenti che richiedono l’accesso al Context: – eseguire Intent, lanciare Activity e Service – accedere al File System e ContentProvider • Esempio: AndroidKeyValueStore
    • Unit testing AndroidKeyValueStore Context
    • Unit testing public class AndroidKeyValueStore { private SQLiteDatabase dbStore; private DatabaseHelper mDatabaseHelper; public AndroidKeyValueStore(Context context) { mDatabaseHelper = new DatabaseHelper(context, "dbname”, "tablename"); open(); // Create table dbStore.execSQL(...); close(); } public void open() { if(dbStore == null) { dbStore = mDatabaseHelper.getWritableDatabase(); } }
    • 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(...); } }
    • Unit testing AndroidTestCase AndroidKeyValueStoreTest
    • Unit testing public 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"); } }
    • 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>
    • Unit testing AndroidTestCase ApplicationTestCase ServiceTestCase ProviderTestCase
    • Unit testing InstrumentationTestCase ActivityInstrumentationTestCase Instrumentation = Functional testing !!
    • Unit testing Generazione di test suite per raggruppare e classificare i TestCase: TestSuite MyTestSuite TestSuiteBuilder
    • 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(); } }
    • Unit testing Struttura dei test: > AndroidManifest.xml src res tests > AndroidManifest.xml src > com > myapp > AllTests.java SomeTests.java package1 > TestCase1.java TestCase2.java package2 > AndroidTestCase1.java AndroidTestCase2.java
    • 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>
    • Unit testing Eseguire 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
    • 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)
    • Unit testing • Eseguire i test direttamente dall’emulatore:
    • Functional testing • Componenti che hanno interfaccia utente: Activity ActivityInstrumentationTestCase • Integration testing sull’intera applicazione: Instrumentation
    • Functional testing • Componenti che hanno interfaccia utente: Activity InstrumentationTestCase ActivityInstrumentationTestCase
    • Functional testing ActivityInstrumentationTestCase2<T extends Activity> • Testing isolato per una singola Activity • Tramite l’oggetto Instrumentation si può interagire con l’interfaccia utente • È possibile utilizzare le TouchUtils per simulare eventi touch
    • Functional testing • Instrumentation: – sendCharacterSync(int keyCode) – sendKeySync(KeyEvent event) – sendPointerSync(MotionEvent event) – sendStringSync(String text) – setInTouchMode(boolean inTouch)
    • 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)
    • Functional testing • Calculator
    • Functional testing Activity Calculator Logic
    • Functional testing ActivityInstrumentationTestCase<Calculator> CalculatorTest
    • Functional testing public 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(); }
    • Functional testing private 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(); }
    • Functional testing public void testPressSomeKeys() { // Make sure that we clear the output press(KeyEvent.KEYCODE_ENTER); press(KeyEvent.KEYCODE_CLEAR); // 3 + 4 * 5 => 23 press(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"); }
    • Functional testing public void testTapSomeButtons() { // Make sure that we clear the output tap(R.id.equal); tap(R.id.del); // 567 / 3 => 189 tap(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");
    • Functional testing // make sure we can continue calculations also // 189 - 789 => -600 tap(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
    • 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 in azioni effettuate tramite l’oggetto Instrumentation • Implementare un custom Instrumentation ci permette di avere il totale controllo sull’applicazione • Il manifest di test deve contenere una copia del manifest dell’applicazione originale e la definizione di un nuovo Instrumentation per poter eseguire i test di integrazione
    • Functional testing AndroidManifest.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>
    • Functional testing • Definire un linguaggio di alto livello per simulare le azioni dell’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 dagli acceptance tests
    • Functional testing Instrumentation CustomInstrumentation CommandRunner Robot
    • 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
    • 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”
    • 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 complete KeyPress(KeyFire) WaitForSyncToComplete(2,20) # Verify Exchanged Data CheckExchangedData("Contacts",1,0,0,0,0,0) RefreshServer() # Verify if the contact is added on the Server CheckNewContactOnServer("Andrea", "Bianchi", true)
    • 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
    • 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>
    • Q&A