Your SlideShare is downloading. ×
0
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:
     – Compone...
Introduzione al TDD
Perché fare TDD in Android:

1. Scrivere codice che funziona :)
2. Scrivere i test prima dell’implemen...
Unit testing vs Functional testing
•   Unit tests:
     – Servono a verificare che tutte le componenti di un programma
    ...
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

•  ...
Unit testing
•   Componenti indipendenti da Android: JUnit classico




                            TestCase




•   Aiuta...
Unit testing
•   MorseCode (Android SDK)
Unit testing


                    = dipendende da Android
       Activity
                    = indipendende da Android

...
Unit testing
class MorseCodeConverter {
   static   final   long   SPEED_BASE = 100;
   static   final   long   DOT = SPEE...
Unit testing

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



                  TestCase




           MorseCodeConverterTest
Unit testing

public class MorseCodeConverterTest extends TestCase {

    public void testCharacterS() throws Exception {
...
Unit testing
• Componenti dipendenti da Android:



                          AndroidTestCase




• Viene utilizzato per l...
Unit testing




    AndroidKeyValueStore   Context
Unit testing
public class AndroidKeyValueStore {

    private SQLiteDatabase dbStore;
    private DatabaseHelper mDatabase...
Unit testing

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

    public void put(String...
Unit testing



               AndroidTestCase




           AndroidKeyValueStoreTest
Unit testing
public class AndroidKeyValueStoreTest extends AndroidTestCase {

    private AndroidKeyValueStore store;

   ...
Unit testing
•   Test di macro componenti in ambiente controllato:
     – Controllo globale sul ciclo di vita del componen...
Unit testing


                      AndroidTestCase




ApplicationTestCase   ServiceTestCase   ProviderTestCase
Unit testing


               InstrumentationTestCase




          ActivityInstrumentationTestCase




          Instrume...
Unit testing
Generazione di test suite per raggruppare e classificare i TestCase:



               TestSuite




         ...
Unit testing
Includere tutti i test case appartenenti ad un package:

public class SomeTests extends TestSuite {
    publi...
Unit testing
Struttura dei test:

> AndroidManifest.xml
  src
  res
  tests > AndroidManifest.xml
          src > com > my...
Unit testing
AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.my...
Unit testing
Eseguire test suite con adb (Android Debug Bridge):

•   Eseguire tutti i test:
     – adb shell am instrumen...
Unit testing
•   Classificare per dimensione:
     – adb shell am instrument -w -e size small
       com.myapp/android.test...
Unit testing
•   Eseguire i test direttamente dall’emulatore:
Functional testing
•   Componenti che hanno interfaccia utente: Activity



                   ActivityInstrumentationTest...
Functional testing
•   Componenti che hanno interfaccia utente: Activity



                      InstrumentationTestCase
...
Functional testing
ActivityInstrumentationTestCase2<T extends Activity>


•   Testing isolato per una singola Activity
•  ...
Functional testing
•   Instrumentation:
    – sendCharacterSync(int keyCode)
    – sendKeySync(KeyEvent event)
    – sendP...
Functional testing
•   TouchUtils:
     – clickView(InstrumentationTestCase test, View v)
     – drag(InstrumentationTestC...
Functional testing
•   Calculator
Functional testing



        Activity




       Calculator    Logic
Functional testing



      ActivityInstrumentationTestCase<Calculator>




                   CalculatorTest
Functional testing
public class CalculatorTest extends
    ActivityInstrumentationTestCase<Calculator> {

   Calculator mA...
Functional testing
  private void press(int keycode) {
      mInst.sendKeyDownUpSync(keycode);
  }

  private boolean tap(...
Functional testing
 public void testPressSomeKeys() {

      // Make sure that we clear the output
      press(KeyEvent.KE...
Functional testing
  public void testTapSomeButtons() {

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

         // make sure we can continue calculations also
         // 189 - 789 => -600
         tap(R.i...
Functional testing
•   Integration testing sull’intera applicazione:
     – Definire un linguaggio di alto livello per test...
Functional testing
AndroidManifest.xml

<manifest package="com.myapp">

    <application android:label=”MyApp">
        <a...
Functional testing
•   Definire un linguaggio di alto livello per simulare le azioni
    dell’utente (esempio: Funambol int...
Functional testing


    Instrumentation




 CustomInstrumentation   CommandRunner   Robot
Functional testing
•   CustomInstrumentation:
    – Carica lo script di test e lo esegue tramite il CommandRunner
•   Comm...
Functional testing
•   Acceptance test case:
    1. “On Device add a record in the Contacts section filling in all possible...
Functional testing
# Create on Device side a new Contact (Andrea Bianchi)
CreateEmptyContact()
SetContactField(FirstName,"...
Functional testing
•   Eseguire i test di integrazione:
     – adb shell am instrument [options] -w
       com.myapp/com.m...
Stress testing
•   Monkey tool:
    – Applicazione command line che può essere eseguita su emulatore o
      device reale
...
Q&A
Upcoming SlideShare
Loading in...5
×

Android Test Driven Development

2,870

Published on

Published in: Technology, Education
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,870
On Slideshare
0
From Embeds
0
Number of Embeds
15
Actions
Shares
0
Downloads
75
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Transcript of "Android Test Driven Development"

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

    Clipping is a handy way to collect important slides you want to go back to later.

×