Testen in Kotlin: Unit-, Integrations- und E2E-Tests auf fortgeschrittenem Niveau

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum fortgeschrittenes Testen die Grundlage für zuverlässigen Kotlin-Code ist

In der modernen Kotlin-Entwicklung ist das Schreiben von Code nur die halbe Miete. Wahre Meisterschaft zeigt sich in der Fähigkeit, zuverlässige, testbare und leicht wartbare Systeme zu erstellen. Grundlegende Unit-Tests kennt jeder, aber wenn ein Projekt auf eine Microservice-Architektur oder komplexe Geschäftslogik anwächst, reichen einfache Überprüfungen nicht mehr aus. Fortgeschrittenes Testen umfasst nicht nur die Isolierung von Klassen, sondern auch die Überprüfung der Interaktion zwischen Komponenten (Integrationstests) und die vollständige Nachbildung von Benutzerszenarien (E2E).

Dieser Artikel richtet sich an Entwickler, die bereits mit den Grundlagen von Kotlin vertraut sind und die Qualität ihrer Projekte auf die nächste Stufe heben möchten. Wir werden drei wichtige Testebenen behandeln: Unit-, Integrations- und E2E-Tests, unter Verwendung moderner Tools: MockK, Testcontainers und Ktor Client. Sie erfahren, wie Sie Tests schreiben, die nicht nur "bestanden werden", sondern Ihren Code tatsächlich vor Regressionen schützen.



1. Unit-Tests: Isolierung und Mocks mit MockK

Unit-Tests sind das Fundament der Testpyramide. Auf fortgeschrittenem Niveau überprüfen wir nicht nur Funktionen, sondern testen komplexe Interaktionen zwischen Objekten mithilfe von Mocks. In Kotlin ist die beste Bibliothek dafür MockK.



1.1. Fortgeschrittene Funktionen von MockK

Im Gegensatz zu Mockito wurde MockK speziell für Kotlin entwickelt und unterstützt Koroutinen, Lambdas und Erweiterungsfunktionen. Betrachten wir ein Beispiel zum Testen eines Dienstes, der ein Repository und Koroutinen verwendet:

// Zu testender Dienstclass UserService(private val repo: UserRepository) {    suspend fun getUserName(id: Long): String {        val user = repo.findById(id)        return user?.name ?: "Unknown"    }}

// Testclass UserServiceTest { private val repo = mockk<UserRepository>() private val service = UserService(repo)

@Test fun `should return user name when user exists`() = runTest { // given coEvery { repo.findById(1L) } returns User(1, "Alice")

// when val result = service.getUserName(1L)

// then assertEquals("Alice", result) coVerify(exactly = 1) { repo.findById(1L) } }

@Test fun `should return Unknown when user not found`() = runTest { // given coEvery { repo.findById(any()) } returns null

// when val result = service.getUserName(99L)

// then assertEquals("Unknown", result) }}

Wichtige Punkte:

  • coEvery und coVerify – für die Arbeit mit suspend-Funktionen.
  • mockk() erstellt einen leeren Mock, spyk() einen partiellen Mock eines realen Objekts.
  • Verwenden Sie slots zum Erfassen von Argumenten.


1.2. Testen von Lambdas und Erweiterungsfunktionen

MockK ermöglicht das Mocken von Erweiterungsfunktionen, was für Frameworks wie Ktor oder Spring WebFlux entscheidend ist:

class StringValidator {    fun String.isValidEmail(): Boolean = this.contains("@")}

// Test@Testfun `test extension mock`() { val validator = mockk<StringValidator>() with(validator) { every { "test@mail.com".isValidEmail() } returns true every { "invalid".isValidEmail() } returns false } assertTrue("test@mail.com".isValidEmail()) assertFalse("invalid".isValidEmail())}


2. Integrationstests: Datenbank und Testcontainers

Integrationstests überprüfen, wie Ihr Code mit realen Abhängigkeiten interagiert: Datenbanken, Nachrichtenwarteschlangen, externen APIs. Hier kommt Testcontainers ins Spiel – eine Bibliothek, die Docker-Container direkt während der Tests hochfährt.



2.1. Einrichtung von Testcontainers mit PostgreSQL

Für eine Spring Boot-Anwendung sieht das so aus:

Blogs

Buchempfehlungen