Kotlin Testing

affaan-m/ECC/docs/tr/skills/kotlin-testing

by affaan-mef648e01899ba3e8dc6371642deaaf64b4477775No license275K starsListed Oct 9, 2026Updated Oct 9, 2026Repository updated 4 days ago

Kotest, MockK, coroutine testi, property-based testing ve Kover coverage ile Kotlin test kalıpları. İdiomatic Kotlin uygulamalarıyla TDD metodolojisini takip eder.

Instructions onlySoftware Development
AI-generated overview

Kotlin testing patterns with Kotest, MockK, coroutine tests, property-based testing and Kover coverage.

What it does
Provides reference patterns and code examples for writing Kotlin tests following TDD: Kotest spec styles, matchers, MockK mocking, coroutine and Flow testing, property-based testing, Ktor testApplication tests, and Kover coverage configuration. It also lists Gradle test commands, coverage targets, best practices and a CI/CD example. It is an instructions-only guide and produces no files itself.
When to use it
Use when writing new Kotlin functions or classes, adding test coverage to existing Kotlin code, applying property-based tests, following a TDD workflow, or configuring Kover coverage in a Kotlin project.
Requirements
A Kotlin project with Gradle, plus Kotest, MockK, kotlinx-coroutines-test, Ktor test utilities and the Kover plugin as applicable. No scripts are shipped; the skill is instructions only.

Kotlin Test Kalıpları

Kotest ve MockK ile TDD metodolojisini takip ederek güvenilir, sürdürülebilir testler yazmak için kapsamlı Kotlin test kalıpları.

Ne Zaman Kullanılır

  • Yeni Kotlin fonksiyonları veya class'lar yazarken
  • Mevcut Kotlin koduna test coverage eklerken
  • Property-based testler uygularken
  • Kotlin projelerinde TDD iş akışını takip ederken
  • Kod coverage için Kover yapılandırırken

Nasıl Çalışır

  1. Hedef kodu belirle — Test edilecek fonksiyon, class veya modülü bul
  2. Kotest spec yaz — Test scope'una uygun bir spec stili seç (StringSpec, FunSpec, BehaviorSpec)
  3. Bağımlılıkları mock'la — Test edilen birimi izole etmek için MockK kullan
  4. Testleri çalıştır (RED) — Testin beklenen hatayla başarısız olduğunu doğrula
  5. Kodu uygula (GREEN) — Testi geçmek için minimal kod yaz
  6. Refactor — Testleri yeşil tutarken implementasyonu iyileştir
  7. Coverage'ı kontrol et — ./gradlew koverHtmlReport çalıştır ve %80+ coverage'ı doğrula

TDD İş Akışı for Kotlin

RED-GREEN-REFACTOR Döngüsü

RED     -> Önce başarısız bir test yazGREEN   -> Testi geçmek için minimal kod yazREFACTOR -> Testleri yeşil tutarken kodu iyileştirREPEAT  -> Sonraki gereksinimle devam et

Kotlin'de Adım Adım TDD

kotlin
// Adım 1: Interface/signature tanımla// EmailValidator.ktpackage com.example.validator
fun validateEmail(email: String): Result<String> {    TODO("not implemented")}
// Adım 2: Başarısız test yaz (RED)// EmailValidatorTest.ktpackage com.example.validator
import io.kotest.core.spec.style.StringSpecimport io.kotest.matchers.result.shouldBeFailureimport io.kotest.matchers.result.shouldBeSuccess
class EmailValidatorTest : StringSpec({    "valid email returns success" {        validateEmail("[email protected]").shouldBeSuccess("[email protected]")    }
    "empty email returns failure" {        validateEmail("").shouldBeFailure()    }
    "email without @ returns failure" {        validateEmail("userexample.com").shouldBeFailure()    }})
// Adım 3: Testleri çalıştır - FAIL doğrula// $ ./gradlew test// EmailValidatorTest > valid email returns success FAILED//   kotlin.NotImplementedError: An operation is not implemented
// Adım 4: Minimal kodu uygula (GREEN)fun validateEmail(email: String): Result<String> {    if (email.isBlank()) return Result.failure(IllegalArgumentException("Email cannot be blank"))    if ('@' !in email) return Result.failure(IllegalArgumentException("Email must contain @"))    val regex = Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")    if (!regex.matches(email)) return Result.failure(IllegalArgumentException("Invalid email format"))    return Result.success(email)}
// Adım 5: Testleri çalıştır - PASS doğrula// $ ./gradlew test// EmailValidatorTest > valid email returns success PASSED// EmailValidatorTest > empty email returns failure PASSED// EmailValidatorTest > email without @ returns failure PASSED
// Adım 6: Gerekirse refactor et, testlerin hala geçtiğini doğrula

Kotest Spec Stilleri

StringSpec (En Basit)

kotlin
class CalculatorTest : StringSpec({    "add two positive numbers" {        Calculator.add(2, 3) shouldBe 5    }
    "add negative numbers" {        Calculator.add(-1, -2) shouldBe -3    }
    "add zero" {        Calculator.add(0, 5) shouldBe 5    }})

FunSpec (JUnit benzeri)

kotlin
class UserServiceTest : FunSpec({    val repository = mockk<UserRepository>()    val service = UserService(repository)
    test("getUser returns user when found") {        val expected = User(id = "1", name = "Alice")        coEvery { repository.findById("1") } returns expected
        val result = service.getUser("1")
        result shouldBe expected    }
    test("getUser throws when not found") {        coEvery { repository.findById("999") } returns null
        shouldThrow<UserNotFoundException> {            service.getUser("999")        }    }})

BehaviorSpec (BDD Stili)

kotlin
class OrderServiceTest : BehaviorSpec({    val repository = mockk<OrderRepository>()    val paymentService = mockk<PaymentService>()    val service = OrderService(repository, paymentService)
    Given("a valid order request") {        val request = CreateOrderRequest(            userId = "user-1",            items = listOf(OrderItem("product-1", quantity = 2)),        )
        When("the order is placed") {            coEvery { paymentService.charge(any()) } returns PaymentResult.Success            coEvery { repository.save(any()) } answers { firstArg() }
            val result = service.placeOrder(request)
            Then("it should return a confirmed order") {                result.status shouldBe OrderStatus.CONFIRMED            }
            Then("it should charge payment") {                coVerify(exactly = 1) { paymentService.charge(any()) }            }        }
        When("payment fails") {            coEvery { paymentService.charge(any()) } returns PaymentResult.Declined
            Then("it should throw PaymentException") {                shouldThrow<PaymentException> {                    service.placeOrder(request)                }            }        }    }})

Kotest Matcher'lar

Temel Matcher'lar

kotlin
import io.kotest.matchers.shouldBeimport io.kotest.matchers.shouldNotBeimport io.kotest.matchers.string.*import io.kotest.matchers.collections.*import io.kotest.matchers.nulls.*
// Eşitlikresult shouldBe expectedresult shouldNotBe unexpected
// String'lername shouldStartWith "Al"name shouldEndWith "ice"name shouldContain "lic"name shouldMatch Regex("[A-Z][a-z]+")name.shouldBeBlank()
// Koleksiyonlarlist shouldContain "item"list shouldHaveSize 3list.shouldBeSorted()list.shouldContainAll("a", "b", "c")list.shouldBeEmpty()
// Null'larresult.shouldNotBeNull()result.shouldBeNull()
// Tiplerresult.shouldBeInstanceOf<User>()
// Sayılarcount shouldBeGreaterThan 0price shouldBeInRange 1.0..100.0
// Exception'larshouldThrow<IllegalArgumentException> {    validateAge(-1)}.message shouldBe "Age must be positive"
shouldNotThrow<Exception> {    validateAge(25)}

MockK

Temel Mocking

kotlin
class UserServiceTest : FunSpec({    val repository = mockk<UserRepository>()    val logger = mockk<Logger>(relaxed = true) // Relaxed: varsayılanları döndürür    val service = UserService(repository, logger)
    beforeTest {        clearMocks(repository, logger)    }
    test("findUser delegates to repository") {        val expected = User(id = "1", name = "Alice")        every { repository.findById("1") } returns expected
        val result = service.findUser("1")
        result shouldBe expected        verify(exactly = 1) { repository.findById("1") }    }
    test("findUser returns null for unknown id") {        every { repository.findById(any()) } returns null
        val result = service.findUser("unknown")
        result.shouldBeNull()    }})

Coroutine Mocking

kotlin
class AsyncUserServiceTest : FunSpec({    val repository = mockk<UserRepository>()    val service = UserService(repository)
    test("getUser suspending function") {        coEvery { repository.findById("1") } returns User(id = "1", name = "Alice")
        val result = service.getUser("1")
        result.name shouldBe "Alice"        coVerify { repository.findById("1") }    }
    test("getUser with delay") {        coEvery { repository.findById("1") } coAnswers {            delay(100) // Async çalışmayı simüle et            User(id = "1", name = "Alice")        }
        val result = service.getUser("1")        result.name shouldBe "Alice"    }})

Coroutine Testi

Suspend Fonksiyonlar İçin runTest

kotlin
import kotlinx.coroutines.test.runTest
class CoroutineServiceTest : FunSpec({    test("concurrent fetches complete together") {        runTest {            val service = DataService(testScope = this)
            val result = service.fetchAllData()
            result.users.shouldNotBeEmpty()            result.products.shouldNotBeEmpty()        }    }
    test("timeout after delay") {        runTest {            val service = SlowService()
            shouldThrow<TimeoutCancellationException> {                withTimeout(100) {                    service.slowOperation() // > 100ms sürer                }            }        }    }})

Flow Testi

kotlin
import io.kotest.matchers.collections.shouldContainInOrderimport kotlinx.coroutines.flow.MutableSharedFlowimport kotlinx.coroutines.flow.toListimport kotlinx.coroutines.launchimport kotlinx.coroutines.test.advanceTimeByimport kotlinx.coroutines.test.runTest
class FlowServiceTest : FunSpec({    test("observeUsers emits updates") {        runTest {            val service = UserFlowService()
            val emissions = service.observeUsers()                .take(3)                .toList()
            emissions shouldHaveSize 3            emissions.last().shouldNotBeEmpty()        }    }
    test("searchUsers debounces input") {        runTest {            val service = SearchService()            val queries = MutableSharedFlow<String>()
            val results = mutableListOf<List<User>>()            val job = launch {                service.searchUsers(queries).collect { results.add(it) }            }
            queries.emit("a")            queries.emit("ab")            queries.emit("abc") // Sadece bu aramayı tetiklemeli            advanceTimeBy(500)
            results shouldHaveSize 1            job.cancel()        }    }})

Property-Based Testing

Kotest Property Testing

kotlin
import io.kotest.core.spec.style.FunSpecimport io.kotest.property.Arbimport io.kotest.property.arbitrary.*import io.kotest.property.forAllimport io.kotest.property.checkAll
class PropertyTest : FunSpec({    test("string reverse is involutory") {        forAll<String> { s ->            s.reversed().reversed() == s        }    }
    test("list sort is idempotent") {        forAll(Arb.list(Arb.int())) { list ->            list.sorted() == list.sorted().sorted()        }    }
    test("serialization roundtrip preserves data") {        checkAll(Arb.bind(Arb.string(1..50), Arb.string(5..100)) { name, email ->            User(name = name, email = "[email protected]")        }) { user ->            val json = Json.encodeToString(user)            val decoded = Json.decodeFromString<User>(json)            decoded shouldBe user        }    }})

Kover Coverage

Gradle Yapılandırması

kotlin
// build.gradle.ktsplugins {    id("org.jetbrains.kotlinx.kover") version "0.9.7"}
kover {    reports {        total {            html { onCheck = true }            xml { onCheck = true }        }        filters {            excludes {                classes("*.generated.*", "*.config.*")            }        }        verify {            rule {                minBound(80) // %80 coverage'ın altında build başarısız            }        }    }}

Coverage Komutları

bash
# Testleri coverage ile çalıştır./gradlew koverHtmlReport
# Coverage eşiklerini doğrula./gradlew koverVerify
# CI için XML raporu./gradlew koverXmlReport
# HTML raporunu görüntüle (OS'nize göre komutu kullanın)# macOS:   open build/reports/kover/html/index.html# Linux:   xdg-open build/reports/kover/html/index.html# Windows: start build/reports/kover/html/index.html

Coverage Hedefleri

Kod TipiHedef
Kritik business mantığı%100
Public API'ler%90+
Genel kod%80+
Generated / config koduHariç tut

Ktor testApplication Testi

kotlin
class ApiRoutesTest : FunSpec({    test("GET /users returns list") {        testApplication {            application {                configureRouting()                configureSerialization()            }
            val response = client.get("/users")
            response.status shouldBe HttpStatusCode.OK            val users = response.body<List<UserResponse>>()            users.shouldNotBeEmpty()        }    }
    test("POST /users creates user") {        testApplication {            application {                configureRouting()                configureSerialization()            }
            val response = client.post("/users") {                contentType(ContentType.Application.Json)                setBody(CreateUserRequest("Alice", "[email protected]"))            }
            response.status shouldBe HttpStatusCode.Created        }    }})

Test Komutları

bash
# Tüm testleri çalıştır./gradlew test
# Belirli test class'ını çalıştır./gradlew test --tests "com.example.UserServiceTest"
# Belirli testi çalıştır./gradlew test --tests "com.example.UserServiceTest.getUser returns user when found"
# Verbose çıktı ile çalıştır./gradlew test --info
# Coverage ile çalıştır./gradlew koverHtmlReport
# Detekt çalıştır (statik analiz)./gradlew detekt
# Ktlint çalıştır (formatlama kontrolü)./gradlew ktlintCheck
# Sürekli test./gradlew test --continuous

En İyi Uygulamalar

YAPILMASI GEREKENLER:

  • ÖNCE testleri yaz (TDD)
  • Proje genelinde Kotest'in spec stillerini tutarlı kullan
  • Suspend fonksiyonlar için MockK'nın coEvery/coVerify'ını kullan
  • Coroutine testi için runTest kullan
  • İmplementasyon değil davranışı test et
  • Pure fonksiyonlar için property-based testing kullan
  • Netlik için data class test fixture'ları kullan

YAPILMAMASI GEREKENLER:

  • Test framework'lerini karıştırma (Kotest seç ve ona sadık kal)
  • Data class'ları mock'lama (gerçek instance'lar kullan)
  • Coroutine testlerinde Thread.sleep() kullanma (advanceTimeBy kullan)
  • TDD'de RED fazını atlama
  • Private fonksiyonları doğrudan test etme
  • Kararsız testleri görmezden gelme

CI/CD ile Entegrasyon

yaml
# GitHub Actions örneğitest:  runs-on: ubuntu-latest  steps:    - uses: actions/checkout@v4    - uses: actions/setup-java@v4      with:        distribution: 'temurin'        java-version: '21'
    - name: Run tests with coverage      run: ./gradlew test koverXmlReport
    - name: Verify coverage      run: ./gradlew koverVerify
    - name: Upload coverage      uses: codecov/codecov-action@v5      with:        files: build/reports/kover/report.xml        token: ${{ secrets.CODECOV_TOKEN }}

Hatırla: Testler dokümantasyondur. Kotlin kodunuzun nasıl kullanılması gerektiğini gösterirler. Testleri okunabilir yapmak için Kotest'in açıklayıcı matcher'larını ve bağımlılıkları temiz mock'lamak için MockK kullanın.

Source and attribution

Source:affaan-m/ECCindocs/tr/skills/kotlin-testingat commitef648e0

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal