Test-Doubles in Android verwenden

Beim Entwerfen der Teststrategie für ein Element oder System gibt es drei Testaspekte:

  • Umfang: Welcher Anteil des Codes wird beim Test berücksichtigt? Mit Tests kann eine einzelne die gesamte Anwendung oder irgendwo dazwischen. Die Der getestete Umfang wird getestet und wird im Allgemeinen als Person unter Test, aber auch unter System Under Test oder Unit Under Test.
  • Geschwindigkeit: Wie schnell wird der Test ausgeführt? Die Testgeschwindigkeiten können von Millisekunden abweichen auf mehrere Minuten.
  • Fidelity: Wie „real“ ist der Test? Wenn ein Teil des Codes die eine Netzwerkanfrage stellen muss, ob der Testcode Netzwerkanfrage senden oder das Ergebnis fälschen? Wenn der Test tatsächlich mit dem Netzwerk verbunden ist, bedeutet dies, dass es eine höhere Genauigkeit hat. Der Nachteil ist, der Test kann länger dauern, zu Fehlern führen, wenn das Netzwerk ausgefallen ist, oder kostspielig sein.

Unter Was du testen solltest, erfährst du, wie du deine Teststrategie festlegen kannst.

Isolation und Abhängigkeiten

Wenn Sie ein Element oder ein Elementsystem testen, geschieht dies isoliert. Für Um ein ViewModel zu testen, müssen Sie beispielsweise keinen Emulator und keine Benutzeroberfläche starten da dies nicht vom Android-Framework abhängt (oder sollte nicht).

Es kann jedoch sein, dass die Testperson von anderen abhängen, damit sie funktioniert. Für kann ein ViewModel mit einem Daten-Repository funktionieren.

Wenn Sie eine Abhängigkeit für eine Testperson angeben müssen, ein test Double (oder Testobjekt) erstellt. Test-Doubles sind Objekte, wie Komponenten in Ihrer App aussehen und funktionieren. Sie werden im Test erstellt, um bestimmte Verhaltensweisen oder Daten liefern. Die Hauptvorteile sind, dass sie Ihre schneller und einfacher zu testen.

Arten von Test-Doubles

Es gibt verschiedene Arten von Test-Doubles:

Fake Ein Test-Double mit einem "funktionierenden" Implementierung der Klasse. Sie wird jedoch so implementiert, dass sie zwar für Tests geeignet, aber nicht für die Produktion geeignet ist.

Beispiel: eine In-Memory-Datenbank.

Fakes erfordern kein Mocking-Framework und sind leicht. Diese werden bevorzugt.

Modell Ein Test-Double, das sich so verhält, wie Sie es programmieren, und das Erwartungen an seine Interaktionen hat. Simulationen schlagen Tests fehl, wenn ihre Interaktionen nicht den von Ihnen definierten Anforderungen entsprechen. Um all dies zu erreichen, werden Modelle in der Regel mit einem Mocking-Framework erstellt.

Beispiel: Überprüfen, ob eine Methode in einer Datenbank genau einmal aufgerufen wurde.

Stub Ein Test-Double, das sich wie Sie programmiert, aber keine Erwartungen an die Interaktionen hat. Wird normalerweise mit einem Mocking-Framework erstellt. Fakes werden der Einfachheit halber gegenüber Stubs bevorzugt.
Dummy Ein Test-Double, das übergeben, aber nicht verwendet wird, z. B. wenn es nur als Parameter bereitgestellt werden muss.

Beispiel: eine leere Funktion, die als Klick-Callback übergeben wird.

Spy – Susan Cooper Undercover Ein Wrapper über einem realen Objekt, das ähnlich wie bei Modellen auch einige zusätzliche Informationen verfolgt. Sie werden normalerweise vermieden, um die Komplexität zu erhöhen. Fakes oder Spots werden daher gegenüber Spioninnen bevorzugt.
Schatten Eine Fälschung, die in Robolectric verwendet wird.

Beispiel mit einer Fälschung

Angenommen, Sie möchten einen Einheitentest für ein ViewModel durchführen, das von einer Schnittstelle abhängt. UserRepository. Der Name des ersten Nutzers wird in einer UI angezeigt. Sie können ein fiktives Test-Double zu erstellen, indem Sie die Schnittstelle implementieren und bekannte Daten.

object FakeUserRepository : UserRepository {
    fun getUsers() = listOf(UserAlice, UserBob)
}

val const UserAlice = User("Alice")
val const UserBob = User("Bob")

Diese fiktive UserRepository muss nicht von den lokalen und Remote-Daten abhängig sein. Quellen, die die Produktionsversion verwenden würde. Die Datei befindet sich in der Testquelle. festgelegt und nicht mit der Produktions-App ausgeliefert.

<ph type="x-smartling-placeholder">
</ph> Eine fiktive Abhängigkeit kann bekannte Daten zurückgeben, ohne von Remote-Datenquellen abhängig zu sein.
Abbildung 1: Eine fiktive Abhängigkeit in einem Einheitentest.

Mit dem folgenden Test wird bestätigt, dass ViewModel den ersten Nutzer korrekt anzeigt. Name der Ansicht hinzufügen.

@Test
fun viewModelA_loadsUsers_showsFirstUser() {
    // Given a VM using fake data
    val viewModel = ViewModelA(FakeUserRepository) // Kicks off data load on init

    // Verify that the exposed data is correct
    assertEquals(viewModel.firstUserName, UserAlice.name)
}

Das Ersetzen von UserRepository durch eine Fälschung ist in einem Unittest einfach, da die ViewModel wird vom Tester erstellt. Es kann jedoch schwierig sein, in größeren Tests.

Ersetzen von Komponenten und Abhängigkeitsinjektion

Wenn Tests keine Kontrolle über die Erstellung der zu testenden Systeme haben, Das Ersetzen von Komponenten für Test-Doubles ist aufwendiger und erfordert die Architektur der App einem testbaren Design folgen.

Selbst große End-to-End-Tests können von Test-Doubles wie instrumentierten UI-Test, bei dem Sie einen vollständigen User Flow in Ihrer App durchlaufen. In In diesem Fall empfiehlt es sich, den Test hermetisch zu machen. Durch einen hermetischen Test wird verhindert, alle externen Abhängigkeiten, z. B. das Abrufen von Daten aus dem Internet. Dieses verbessert die Zuverlässigkeit und Leistung.

<ph type="x-smartling-placeholder">
</ph>
Abbildung 2: Ein großer Test, der einen Großteil der App abdeckt und Remote-Daten fälscht.

Sie können Ihre App so gestalten, dass diese Flexibilität manuell erreicht wird, wir empfehlen jedoch, Sie verwenden ein Abhängigkeitsinjektions-Framework wie Hilt, um Komponenten zu ersetzen. zum Testen in Ihrer App an. Weitere Informationen findest du im Leitfaden zum Testen von Hilt.

Robolektrik

Auf Android-Geräten können Sie das Robolectric-Framework verwenden, das einen speziellen Typ von Test-Double. Mit Robolectric können Sie Tests oder in Ihrer Continuous Integration-Umgebung. Es wird ein reguläre JVM, ohne Emulator oder Gerät. Es simuliert die Steigerung der Aufrufe, das Laden von Ressourcen und andere Teile des Android-Frameworks mit Test-Doubles werden als Schatten bezeichnet.

Robolectric ist ein Simulator und sollte daher keine einfachen Einheitentests ersetzen um Kompatibilitätstests durchzuführen. Schnelligkeit und senkt Kosten in einigen Fällen mit geringerer Genauigkeit. Es empfiehlt sich, UI-Tests ist mit Robolectric- und instrumentierten Tests kompatibel und kann entscheiden, wann je nachdem, ob ein Funktions- oder Kompatibilitätstest durchgeführt werden muss. Beide Espresso und Compose-Tests können auf Robolectric ausgeführt werden.