Was man über Unit Tests in Android wissen sollte

JUnit für Android

Arne Limburg und Lars Röwekamp

Das Schreiben von „echten“ Unit Tests ist in Android eine deutlich größere Herausforderung als in der Java-Server-Entwicklung. Zum einen sind Android-Applikationen in der Regel deutlich GUI-lastiger und zum anderen haben Android-Komponenten erheblich mehr hart „verdrahtete“ Abhängigkeiten, die durch das Android-Framework vorgegeben werden. Mechanismen, diese Abhängigkeiten mit Mock-Objekten zu befüllen, sind daher ein Muss für Unit Tests in Android. „Good to know“ zeigt, wie man hier vorgehen kann.

JUnit ist das Unit-Testing-Framework im Java-Umfeld und wird seit Jahren erfolgreich zum Erstellen von automatisiert durchführbaren Unit Tests verwendet, mit denen man nicht nur eine verbesserte Codequalität, sondern auch Regressionssicherheit gewinnt. Ebenfalls seit einigen Jahren gibt es dazu mächtige Mocking-Frameworks, mit denen die Abhängigkeiten von zu testenden Komponenten durch Mocks ersetzt werden können. Was liegt also näher, als JUnit auch in Android zu verwenden? Schließlich bietet ja das Android SDK umfangreichen Support für JUnit-Tests von Android-Applikationen. Wenn man also seine Implementierung einer Android-Komponente (z. B. eine Activity) testen will, kann man sich aus einer der zahlreichen Testoberklassen des Android SDKs bedienen (z.B. ActivityUnitTestCase). Zwar basiert das Android-JUnit-Framework auf JUnit 3 (und nicht auf der aktuellen Version 4), das lässt sich aber verschmerzen.

Die erstellten Tests werden in einer separaten Testapplikation gepackt und auf dem Emulator deployt, wo sie von dem Android JUnit Runner ausgeführt werden können. Die Testbasisklassen erzeugen die jeweils zu testende Android-Komponente und stellen mehr oder weniger eine isolierte Ablaufumgebung zur Verfügung. Dieses „mehr oder weniger“ ist allerdings schon der erste Haken. Ist doch die Grundidee eines Unit Tests das komplett isolierte Testen einer Klasse (oder sogar einer Methode). Die komplette Isolation einer Android-Komponente, zum Beispiel einer Activity, ist aber aufgrund der vielen Abhängigkeiten zum Android-Framework gar nicht so leicht möglich. Will man zum Beispiel testen, ob bei einem Click auf ein Menü korrekterweise ein Zugriff auf einen ContentProvider (bzw. auf den ContentResolver mit den entsprechenden Parametern) passiert, hätte man am liebsten gleich Mocks für eine ganze Reihe von Objekten (Listing 1): Man bräuchte zunächst ein Mock-Objekt für den Parameter – MenuItem. Da innerhalb der zu testenden Methode auf diesem MenuItem auch noch die Methode getMenuInfo()aufgerufen wird, benötigt man für deren Rückgabewert auch ein Mock. Auch die Abhängigkeit zu der nicht näher definierten Komponente detail sollte eventuell durch ein Mock-Objekt ausgetauscht werden. Zu guter Letzt müsste dann noch der ContentResolver durch ein Mock ersetzt werden.

Listing 1: zu testende Activity
public class MyActivity extends Activity { 
  
  ...
  public boolean onContextItemSelected(MenuItem item) {
    AdapterContextMenuInfo info
      = (AdapterContextMenuInfo) item.getMenuInfo();
    int rowId = info.id;
    if (item.getItemId() == R.id.menu_open) {
      Uri base = MyContentProvider.CONTENT_URI;
      Uri uri = ContentUris.withAppendedId(base, rowId);
      Cursor cursor
        = getContentResolver().query(uri, null, null, null, null);
      if (cursor.moveToFirst()) {
        detail.setText(cursor.getString(0));
      }
    }
  }
}
Mock-Objekte im Emulator

Zwar bietet das Android SDK Mock-Objekte für die gängigen Komponenten, so gibt es für unseren Zweck zum Beispiel den MockContentResolver. Dieser ist allerdings nur teilweise ein echtes Mock-Objekt: Er benötigt einen ContentProvider, um korrekt funktionieren zu können. Ein MockContentProvider ist im SDK auch vorhanden. Von diesem muss man aber eine Unterklasse erstellen, mit der man das gewünschte Verhalten testen kann. Aber spätestens, wenn man dann nach einem Mock für das MenuItem sucht, weil man dieses nicht direkt instanziieren kann, wird man nicht mehr fündig und wünscht sich ein Mocking-Framework wie EasyMock [1] oder Mockito [2]. Leider arbeiten diese Frameworks mit Java Reflection, die bekanntermaßen nicht auf der Dalvik VM, also auch nicht im Emulator, funktioniert. Somit scheiden die Frameworks zur Erzeugung von Mock-Objekten im Emulator aus. Abhilfe schafft das Framework Android Mock. Es erzeugt die Mock-Objekte bereits zur Compile-Zeit – also noch in der Java-Welt – und deployt sie dann gemeinsam mit dem Testprojekt auf den Emulator. Android Mock verwendet die Syntax von EasyMock und sollte somit jedem, der bereits mit EasyMock gearbeitet hat, vertraut vorkommen.

Geschrieben von
Arne Limburg und Lars Röwekamp
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.