Integrationstests überprüfen die Korrektheit der Interaktion zwischen den Komponenten einer mobilen Anwendung — Modulen, Diensten, Datenbanken und externen APIs. Im Gegensatz zu Unit-Tests, die jede Komponente isolieren, decken Integrationstests Fehler an den Schnittstellen auf: Inkompatibilität von Datenformaten, Fehler bei der Parameterübertragung und fehlerhafte Verarbeitung von Serverantworten. Laut Martin Fowler, 2018 decken Integrationstests bis zu 40% der kritischen Fehler ab, die von Unit-Tests übersehen werden, und schaffen Vertrauen in die Stabilität des Systems vor der Veröffentlichung.
Das Wichtigste
Integrationstests sind eine Phase der Softwareüberprüfung, in der die Korrektheit der Interaktion zwischen einzelnen Modulen oder Subsystemen einer Anwendung bewertet wird. Während Unit-Tests jede Komponente isoliert prüfen, führen Integrationstests diese Komponenten zusammen und überprüfen, wie sie im Verbund funktionieren. Typische Szenarien umfassen die Datenübertragung zwischen der Netzwerkschicht und dem Repository, das Schreiben in eine Datenbank über ORM und die Verarbeitung von Antworten von Drittanbieter-APIs.
Im Kontext der mobilen Entwicklung decken Integrationstests die Interaktionen zwischen der UI-Schicht, der Geschäftslogik und den Datenquellen ab. Ein Test kann beispielsweise überprüfen, dass die Anwendung nach dem Klicken auf die Schaltfläche „Login“ eine Anfrage an den Server sendet, ein Token erhält und es im lokalen Speicher ablegt. Eine solche Überprüfung bestätigt, dass die Komponentenkette fehlerfrei funktioniert.
Laut dem World Quality Report 2023 reduzieren Unternehmen, die regelmäßig Integrationstests einsetzen, die Anzahl der Produktionsvorfälle um 35% im Vergleich zu Projekten, die sich nur auf Unit-Tests verlassen. Dies macht Integrationstests zu einem obligatorischen Element der Qualitätssicherungsstrategie in der kommerziellen Entwicklung.
Mobile Anwendungen bestehen aus vielen miteinander verbundenen Komponenten: Netzwerkanfragen, lokale Datenbanken, Push-Benachrichtigungen, Systemdienste und Drittanbieter-SDKs. Jede dieser Komponenten wird separat entwickelt, aber zur Laufzeit tauschen sie in Echtzeit Daten aus. Integrationstests decken Fehler auf, die bei der isolierten Modulprüfung nicht gefunden werden können.
Zu den typischen Problemen, die durch Integrationstests entdeckt werden, gehören Datentypinkompatibilität zwischen der API und dem Anwendungsmodell, JSON-Serialisierungsfehler, falsche Behandlung von Netzwerk-Timeouts und Fehler beim gleichzeitigen Datenbankzugriff über Room oder Core Data. Ohne Integrationstests gelangen solche Fehler in die Produktion und treten nur bei echten Benutzern auf.
Die Forschung des Google Testing Blog (2021) zeigt, dass die Kosten für die Behebung eines während der Integrationstests entdeckten Fehlers 5-mal niedriger sind als nach der Veröffentlichung. Dies liegt daran, dass der Entwickler in frühen Phasen den vollständigen Fehlerkontext hat und ihn ohne dringenden Hotfix-Zyklus beheben kann. Die Investition von Zeit in das Schreiben von Integrationstests zahlt sich durch geringere Wartungskosten und erhöhtes Benutzervertrauen aus.
Es gibt drei Hauptansätze zur Organisation von Integrationstests: Big Bang, Bottom-Up und Top-Down. Die Wahl der Strategie hängt von der Projektgröße, der Anwendungsarchitektur und der Verfügbarkeit der Komponenten zum Zeitpunkt der Testerstellung ab. Jeder Ansatz hat seine Vor- und Nachteile, die bei der Planung der Testabdeckung zu berücksichtigen sind.
Big Bang — ein Ansatz, bei dem alle Systemkomponenten gleichzeitig verbunden werden, wonach ein allgemeiner Testlauf durchgeführt wird. Diese Methode ist einfach zu implementieren: Es müssen keine Stubs geschrieben oder einzelne Module emuliert werden. Wenn jedoch ein Fehler entdeckt wird, ist es schwierig zu bestimmen, welche Komponente ihn verursacht hat. Big Bang ist bei kleinen Projekten mit einfacher Architektur gerechtfertigt, bei denen die Anzahl der Module fünf nicht überschreitet.
Bottom-Up — eine Strategie, bei der Integrationstests mit niedrigen Komponenten beginnen: Datenbank, Netzwerkschicht, Systemdienste. Nach der Überprüfung jeder Ebene werden nach und nach höhere Module angeschlossen — Repositories, Use-Case-Klassen und ViewModels. Der Hauptvorteil ist die frühe Erkennung von Fehlern in den grundlegenden Anwendungsschichten, wodurch das Risiko von Kaskadenfehlern in späteren Entwicklungsphasen verringert wird.
Top-Down — ein Ansatz, bei dem die Tests mit Komponenten der oberen Ebene beginnen — UI-Bildschirme und Navigation, während die darunterliegenden Module mit Stubs oder Mocks simuliert werden. Dies ermöglicht die Überprüfung von Benutzerszenarien, bevor die Serverseite oder Datenbank vollständig implementiert ist. Top-Down ist besonders nützlich bei der parallelen Entwicklung von Client- und Serverteilen, wenn das Backend noch nicht für die echte Integration bereit ist.
Für Integrationstests mobiler Anwendungen wird eine Reihe spezialisierter Werkzeuge verwendet, die in drei Kategorien unterteilt sind: Bibliotheken zur Serveremulation, Frameworks für die Datenbankarbeit und Mittel zur Überprüfung von Systemdiensten. Die Wahl des konkreten Werkzeugs hängt von der Plattform — Android oder iOS — und dem Technologie-Stack des Projekts ab.
Sehen wir uns praktische Beispiele für Integrationstests für Android und iOS an. Für die Android-Plattform verwenden wir MockWebServer zusammen mit JUnit, für iOS — XCTest mit der OHHTTPStubs-Bibliothek. Beide Beispiele überprüfen das Szenario des Empfangs von Daten von einer API und deren Speicherung in einem lokalen Repository.
Dieser Test überprüft, dass eine Retrofit-Anfrage an den emulierten Server korrektes JSON zurückgibt und das Repository die Antwort in ein Domain-Modell umwandelt. MockWebServer fängt die Anfrage ab und gibt das angegebene JSON zurück, woraufhin der Test das erwartete Ergebnis mit dem tatsächlichen vergleicht.
class UserRepositoryTest {
private val mockServer = MockWebServer()
@Before
fun setup() {
mockServer.start()
}
@Test
fun fetchUser_returnsCorrectData() {
val json = "{ \`"id\`": 1, \`"name\`": \`"Alice\`" }"
mockServer.enqueue(MockResponse()
.setBody(json)
.setResponseCode(200))
val repository = UserRepository(
createRetrofit(mockServer.url("/").toString()))
val user = repository.fetchUser(1)
assertEquals(1, user.id)
assertEquals("Alice", user.name)
}
@After
fun tearDown() {
mockServer.shutdown()
}
}
Für iOS verwendet ein ähnlicher Test OHHTTPStubs, um URL-Anfragen abzufangen. Die Bibliothek ersetzt die Serverantwort auf der System-Framework-Ebene des URL Loading System, sodass jede Netzwerkbibliothek — URLSession, Alamofire oder Moya — getestet werden kann.
import XCTest
import OHHTTPStubs
import OHHTTPStubsSwift
class UserRepositoryTests: XCTestCase {
func testFetchUser_returnsCorrectData() {
stub(condition: isPath("/users/1")) { _ in
return HTTPStubsResponse(
jsonObject: ["id": 1, "name": "Alice"],
statusCode: 200,
headers: nil
)
}
let repository = UserRepository()
let expectation = expectation(description: "fetch user")
repository.fetchUser(id: 1) { user in
XCTAssertEqual(user.id, 1)
XCTAssertEqual(user.name, "Alice")
expectation.fulfill()
}
waitForExpectations(timeout: 2.0)
}
}
Effektive Integrationstests erfordern die Einhaltung einer Reihe von Praktiken, die die Teststabilität erhöhen und die Wartungskosten senken. Isolieren Sie externe Abhängigkeiten: Verwenden Sie In-Memory-Datenbanken anstelle von Produktionsinstanzen und emulieren Sie Drittanbieter-APIs mit Stub-Bibliotheken. Dadurch werden nicht deterministische Fehler vermieden, die durch Netzwerkverfügbarkeit oder den Zustand externer Dienste verursacht werden.
Bewahren Sie die Testunabhängigkeit: Jeder Integrationstest sollte isoliert arbeiten, ohne von den Ergebnissen anderer Tests abhängig zu sein. Verwenden Sie @Before- und @After-Annotationen in JUnit oder setUp und tearDown in XCTest, um die Testumgebung vorzubereiten und zu bereinigen. Dies verhindert gegenseitige Testbeeinflussung und vereinfacht die Fehlerdiagnose.
Decken Sie Grenzfälle ab: Integrationstests sollten nicht nur erfolgreiche Szenarien (Happy Path) überprüfen, sondern auch die Fehlerbehandlung — Timeouts, HTTP-4xx- und 5xx-Codes, leere Antworten, fehlerhaftes JSON. Laut dem Google Testing Blog (2022) stehen 60% der Produktionsvorfälle im Zusammenhang mit einer fehlerhaften Behandlung von Grenzfällen, die nicht durch Tests abgedeckt waren.
Häufig gestellte Fragen
Unit-Tests überprüfen eine einzelne Klasse oder Funktion isoliert, indem sie Abhängigkeiten durch Stubs ersetzen. Integrationstests überprüfen die Interaktion mehrerer echter Komponenten — beispielsweise eine Netzwerkverbindung und eine Datenbank gleichzeitig.
Die Ausführung von Integrationstests dauert in der Regel 2 bis 15 Minuten, abhängig von der Anzahl der Tests und der Komplexität der Umgebung. Für große Projekte wird empfohlen, die Tests auf parallele Jobs in einem CI-System aufzuteilen, um die Gesamtprüfzeit vor dem Merge zu verkürzen.
In erster Linie werden Integrationstests für die Netzwerkschicht, die Datenbank und die Systemdienste — Benachrichtigungen, Kamera, Geolokalisierung — geschrieben. API-Anfragen an das Backend und lokale Speicheroperationen liefern den höchsten ROI, da diese Komponenten am häufigsten zu Quellen von Regressionen werden.
Für einen einzelnen Bildschirm reichen Unit-Tests für das ViewModel und UI-Tests aus. Integrationstests für einen einzelnen Bildschirm sind nur dann gerechtfertigt, wenn der Bildschirm mit mehreren Datenquellen interagiert — beispielsweise Antworten von zwei verschiedenen APIs kombiniert oder Daten gleichzeitig in das Netzwerk und die lokale Datenbank schreibt.
Integrationstests sollten bei jedem Pull Request in der CI-Pipeline und vor größeren Releases ausgeführt werden. Es wird auch empfohlen, den vollständigen Satz von Integrationstests nachts (Nightly Build) auszuführen, um Fehler im Zusammenhang mit Änderungen an Abhängigkeiten oder der Testumgebung zu erkennen.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch