JUnit für Android

Testen ohne Emulator

Wenn das Erzeugen von Mock-Objekten im Android Emulator solche Probleme bereitet, stellt sich natürlich die Frage, warum man die Unit Tests überhaupt im Emulator ausführen muss. Neben der fehlenden Unterstützung für Java Reflection hat die Testausführung im Emulator noch einen erheblichen weiteren Nachteil: Sie ist sehr langsam. Insbesondere die Tatsache, dass die Anwendung vor jedem Test neu gepackt, auf den Emulator gespielt und dort auch noch gestartet werden muss, bremst die Entwicklung erheblich aus. Was liegt also näher, als auf die Idee zu kommen, auf die Emulator-Tests zu verzichten und die Tests stattdessen direkt in JUnit als Java-Projekt auszuführen.

Leider stößt man hier bereits beim Initialisieren der zu testenden Klasse auf ein größeres Problem: Der Aufruf von new MyActivity()wirft eine java.lang.RuntimeException. Der Grund dafür ist, dass das android.jar, das mit dem SDK ausgeliefert wird, keine kompletten Class-Dateien enthält, sondern lediglich solche, bei denen Methodenaufrufe und auch der Konstruktoraufruf besagte Exception werfen. Erst mit Android auf dem Emulator, das den Dalvik Bytecode enthält, sind die Klassen ausführbar.

Muss man daher komplett auf das Testen außerhalb des Emulators verzichten? Ein einfacher Ansatz wäre, zu versuchen, die Anwendung so zu schreiben, dass man die Verwendung von Android-spezifischen Abhängigkeiten komplett kapselt und die Businesslogik von Android freihält. Diese Businesslogik könnte man dann mit JUnit testen. Aber bereits die nähere Betrachtung des hier gezeigten kleinen Beispiels lässt erahnen, dass der meiste Code in Android-Applikationen Android-spezifisch ist und dass der Ansatz, nur Android-freien Code zu testen, zu einer sehr geringen Testabdeckung führt. Wenigstens die Instanziierung von Android-spezifischen Klassen in JUnit sollte möglich sein, um diese für die Tests außerhalb des Emulators zu verwenden.

Hier kommt das Framework Robolectric [4] ins Spiel. Es hat sich zum Ziel gesetzt, genau das zu ermöglichen. Mit der richtigen, auf der Robolectric gut dokumentierten Testkonfiguration lassen sich Android-Komponenten auch direkt in Java beziehungsweise in JUnit-Tests erzeugen – ohne die hässliche RuntimeException. Dank Robolectric werden „echte“ JUnit-Tests, auch unter Verwendung von EasyMock oder Mockito, möglich (Listing 2). Robolectric geht sogar noch einen Schritt weiter und ermöglicht in gewissem Rahmen auch UI-Tests. Hierzu wird in Java das Android-Layout geparst und der View-Objektbaum aufgebaut. Dank dieses Features funktioniert dann sogar Code, der den Click auf die Liste testet.

Listing 2: Test mit Robolectric und Mockito
import static org.mockito.Mockito.*;

@RunWith(RobolectricTestRunner.class)
public class MyActivityTest { 
  
  .
  
  @Test
  public void onContextItemSelected() {
    ContentResolver resolver = mock(ContentResolver.class);
    Cursor cursor = mock(Cursor.class);
    MenuItem item = mock(MenuItem.class);
    AdapterContextMenuInfo info = mock(AdapterContextMenuInfo.class);
    TextView text = mock(TextView.class);

    when(item.getMenuInfo()).thenReturn(info);
    when(item.getItemId()).thenReturn(R.id.menu_open);
    Uri expectedUri = .;
    when(resolver.query(expectedUri, null, null, null, null))
      .thenReturn(cursor);
    when(cursor.moveToFirst()).thenReturn(true);
    when(cursor.getString(0)).thenReturn("Test");

    MyActivity activity = getActivity();
    activity.setContentResolver(resolver);
    activity.setDetail(text);

    activity.onContextItemSelected(item);

    verify(resolver).query(expectedUri, null, null, null, null);
    verify(text).setText("Test");
  }
}
Fazit

Entwicklung in Android bedeutet nicht automatisch, dass man Tests nur auf dem Emulator oder einem echten Device durchführen kann und dabei auf Mocking-Frameworks verzichten muss. Das Framework Android Mock bietet die Erzeugung von Mock-Objekten für den Emulator. Mittlerweile gibt es auch Lösungen, um gute alte JUnit-Tests ohne Emulator zu schreiben und auszuführen und dabei die gewohnten Mocking-Frameworks aus der Java-Welt (wie EasyMock oder Mockito) zu nutzen. Die größte Herausforderung dabei, nämlich das einfache Instanziieren von Android-Komponenten in Java, wird durch das Framework Robolectric ermöglicht. Es bietet beim Testing außerhalb des Emulators noch weitere interessante Möglichkeiten und ist daher für das Android Testing eine gute Wahl.

Lars Röwekamp ist Geschäftsführer der open knowledge GmbH und berät seit mehr als zehn Jahren Kunden in internationalen Projekten rund um das Thema Enterprise Computing (Twitter: @mobileLarson)

Arne Limburg ist Softwarearchitekt bei der open knowledge GmbH in Oldenburg. Er verfügt über langjährige Erfahrung als Entwickler, Architekt und Consultant im Java-Umfeld und ist auch seit der ersten Stunde im Android-Umfeld aktiv.

Kommentare

Schreibe einen Kommentar

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