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

Like this? Share it with your network

Share

Android Test Driven Development

on

  • 3,747 views

 

Statistics

Views

Total Views
3,747
Views on SlideShare
3,180
Embed Views
567

Actions

Likes
3
Downloads
74
Comments
0

24 Embeds 567

http://sazilla.blogspot.com 339
http://www.whymca.org 56
http://sazilla.blogspot.it 56
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 Presentation Transcript

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