Ein lokaler Test wird direkt auf Ihrer eigenen Workstation und nicht auf einem Android-Gerät oder -Emulator ausgeführt. Daher wird zum Ausführen von Tests Ihre lokale Java Virtual Machine (JVM) anstelle eines Android-Geräts verwendet. Mit lokalen Tests können Sie die Logik Ihrer App schneller bewerten. Da Sie jedoch nicht mit dem Android-Framework interagieren können, sind die Arten von Tests, die Sie ausführen können, eingeschränkt.
Mit einem Unit-Test wird das Verhalten eines kleinen Codeabschnitts, der zu testenden Einheit, überprüft. Dazu wird der Code ausgeführt und das Ergebnis geprüft.
Unit-Tests sind in der Regel einfach, aber ihre Einrichtung kann problematisch sein, wenn die zu testende Einheit nicht auf Testbarkeit ausgelegt ist:
- Der Code, den Sie prüfen möchten, muss über einen Test zugänglich sein. Beispielsweise können Sie eine private Methode nicht direkt testen. Stattdessen testen Sie die Klasse über ihre öffentlichen APIs.
- Damit Unit-Tests isoliert ausgeführt werden können, müssen die Abhängigkeiten der zu testenden Einheit durch Komponenten ersetzt werden, die Sie steuern, z. B. Fakes oder andere Test-Doubles. Das ist besonders problematisch, wenn Ihr Code vom Android-Framework abhängt.
Informationen zu gängigen Strategien für Unittests in Android finden Sie unter Was getestet werden sollte.
Speicherort für lokale Tests
Standardmäßig werden die Quelldateien für lokale Unittests in module-name/src/test/ abgelegt. Dieses Verzeichnis ist bereits vorhanden, wenn Sie ein neues Projekt mit Android Studio erstellen.
Testabhängigkeiten hinzufügen
Außerdem müssen Sie die Testabhängigkeiten für Ihr Projekt konfigurieren, um die Standard-APIs des JUnit-Testframeworks zu verwenden.
Öffnen Sie dazu die Datei build.gradle des Moduls Ihrer App und geben Sie die folgenden Bibliotheken als Abhängigkeiten an. Verwenden Sie die Funktion testImplementation, um anzugeben, dass sie für das lokale Test-Source-Set und nicht für die Anwendung gelten:
dependencies {
// Required -- JUnit 4 framework
testImplementation "junit:junit:$jUnitVersion"
// Optional -- Robolectric environment
testImplementation "androidx.test:core:$androidXTestVersion"
// Optional -- Mockito framework
testImplementation "org.mockito:mockito-core:$mockitoVersion"
// Optional -- mockito-kotlin
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
// Optional -- Mockk framework
testImplementation "io.mockk:mockk:$mockkVersion"
}
Lokale Unittest-Klasse erstellen
Sie schreiben Ihre lokale Unittest-Klasse als JUnit 4-Testklasse.
Erstellen Sie dazu eine Klasse, die eine oder mehrere Testmethoden enthält, normalerweise in module-name/src/test/. Eine Testmethode beginnt mit der Annotation @Test und enthält den Code, mit dem ein einzelner Aspekt der zu testenden Komponente ausgeführt und überprüft wird.
Das folgende Beispiel zeigt, wie Sie eine lokale Unittest-Klasse implementieren. Die Testmethode emailValidator_correctEmailSimple_returnsTrue() versucht, isValidEmail() zu überprüfen, eine Methode innerhalb der App. Die Testfunktion gibt „true“ zurück, wenn isValidEmail() ebenfalls „true“ zurückgibt.
Kotlin
import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test class EmailValidatorTest { @Test fun emailValidator_CorrectEmailSimple_ReturnsTrue() { assertTrue(EmailValidator.isValidEmail("name@email.com")) } }
Java
import org.junit.Test; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; class EmailValidatorTest { @Test public void emailValidator_CorrectEmailSimple_ReturnsTrue() { assertTrue(EmailValidator.isValidEmail("name@email.com")); } }
Sie sollten lesbare Tests erstellen, mit denen Sie prüfen, ob die Komponenten in Ihrer App die erwarteten Ergebnisse zurückgeben. Wir empfehlen die Verwendung einer Assertionsbibliothek wie junit.Assert, Hamcrest oder Truth. Das Snippet oben ist ein Beispiel für die Verwendung von junit.Assert.
Mockable Android-Bibliothek
Wenn Sie lokale Unittests ausführen, enthält das Android-Gradle-Plug-in eine Bibliothek mit allen APIs des Android-Frameworks, die der in Ihrem Projekt verwendeten Version entsprechen. Die Bibliothek enthält alle öffentlichen Methoden und Klassen dieser APIs, der Code in den Methoden wurde jedoch entfernt. Wenn auf eine der Methoden zugegriffen wird, löst der Test eine Ausnahme aus.
So können lokale Tests erstellt werden, wenn auf Klassen im Android-Framework wie Context verwiesen wird. Noch wichtiger ist, dass Sie ein Mocking-Framework mit Android-Klassen verwenden können.
Android-Abhängigkeiten simulieren
Ein typisches Problem ist, dass eine Klasse eine String-Ressource verwendet. String-Ressourcen können Sie durch Aufrufen der Methode getString() in der Klasse Context abrufen. Bei einem lokalen Test können jedoch weder Context noch die zugehörigen Methoden verwendet werden, da sie zum Android-Framework gehören. Idealerweise wird der Aufruf von getString() aus der Klasse verschoben, aber das ist nicht immer praktikabel. Die Lösung besteht darin, ein Mock oder einen Stub von Context zu erstellen, der immer denselben Wert zurückgibt, wenn die Methode getString() aufgerufen wird.
Mit der Mockable Android-Bibliothek und Mocking-Frameworks wie Mockito oder MockK können Sie das Verhalten von Mocks der Android-Klassen in Ihren Unittests programmieren.
Wenn Sie Ihrem lokalen Unittest mit Mockito ein Mock-Objekt hinzufügen möchten, verwenden Sie dieses Programmiermodell:
- Fügen Sie die Mockito-Bibliotheksabhängigkeit in Ihre
build.gradle-Datei ein, wie unter Testumgebung einrichten beschrieben. - Fügen Sie am Anfang der Definition der Unit-Test-Klasse die Annotation
@RunWith(MockitoJUnitRunner.class)hinzu. Diese Annotation weist den Mockito-Test-Runner an, zu prüfen, ob Sie das Framework korrekt verwenden, und vereinfacht die Initialisierung Ihrer Mock-Objekte. - Wenn Sie ein Scheinobjekt für eine Android-Abhängigkeit erstellen möchten, fügen Sie die Annotation
@Mockvor der Felddeklaration hinzu. - Wenn Sie das Verhalten der Abhängigkeit simulieren möchten, können Sie mit den Methoden
when()undthenReturn()eine Bedingung und einen Rückgabewert angeben, der zurückgegeben wird, wenn die Bedingung erfüllt ist.
Das folgende Beispiel zeigt, wie Sie einen Unit-Test erstellen können, der ein mit Mockito-Kotlin erstelltes Mock-Context-Objekt in Kotlin verwendet.
import android.content.Context
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
private const val FAKE_STRING = "HELLO WORLD"
@RunWith(MockitoJUnitRunner::class)
class MockedContextTest {
@Mock
private lateinit var mockContext: Context
@Test
fun readStringFromContext_LocalizedString() {
// Given a mocked Context injected into the object under test...
val mockContext = mock<Context> {
on { getString(R.string.name_label) } doReturn FAKE_STRING
}
val myObjectUnderTest = ClassUnderTest(mockContext)
// ...when the string is returned from the object under test...
val result: String = myObjectUnderTest.getName()
// ...then the result should be the expected one.
assertEquals(result, FAKE_STRING)
}
}
Weitere Informationen zur Verwendung des Mockito-Frameworks finden Sie in der Mockito API-Referenz und in der Klasse SharedPreferencesHelperTest im Beispielcode. Probieren Sie auch das Codelab zum Testen von Android-Apps aus.
Fehler: „Methode ... nicht simuliert“
Die Mockable Android-Bibliothek löst eine Ausnahme aus, wenn Sie versuchen, auf eine ihrer Methoden mit der Meldung Error: "Method ... not mocked zuzugreifen.
Wenn die ausgelösten Ausnahmen für Ihre Tests problematisch sind, können Sie das Verhalten so ändern, dass Methoden je nach Rückgabetyp entweder null oder null zurückgeben. Fügen Sie dazu die folgende Konfiguration in der build.gradle-Datei auf oberster Ebene Ihres Projekts in Groovy hinzu:
android {
...
testOptions {
unitTests.returnDefaultValues = true
}